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"
38 #include "apr_buckets.h"
40 #include "apr_dbd_internal.h"
42 /* default maximum field size 1 MB */
43 #define FIELDSIZE 1048575
45 struct apr_dbd_prepared_t
{
49 apr_dbd_type_e
*types
;
52 struct apr_dbd_transaction_t
{
60 apr_dbd_transaction_t
* trans
;
64 struct apr_dbd_results_t
{
67 MYSQL_STMT
*statement
;
71 struct apr_dbd_row_t
{
73 apr_dbd_results_t
*res
;
77 /* MySQL specific bucket for BLOB types */
78 typedef struct apr_bucket_lob apr_bucket_lob
;
80 * A bucket referring to a MySQL BLOB
82 struct apr_bucket_lob
{
83 /** Number of buckets using this memory */
84 apr_bucket_refcount refcount
;
85 /** The row this bucket refers to */
86 const apr_dbd_row_t
*row
;
87 /** The column this bucket refers to */
89 /** The pool into which any needed structures should
90 * be created while reading from this bucket */
94 static void lob_bucket_destroy(void *data
);
95 static apr_status_t
lob_bucket_read(apr_bucket
*e
, const char **str
,
96 apr_size_t
*len
, apr_read_type_e block
);
97 static apr_bucket
*apr_bucket_lob_make(apr_bucket
*b
,
98 const apr_dbd_row_t
*row
, int col
,
99 apr_off_t offset
, apr_size_t len
,
101 static apr_bucket
*apr_bucket_lob_create(const apr_dbd_row_t
*row
, int col
,
103 apr_size_t len
, apr_pool_t
*p
,
104 apr_bucket_alloc_t
*list
);
106 static const apr_bucket_type_t apr_bucket_type_lob
= {
107 "LOB", 5, APR_BUCKET_DATA
,
110 apr_bucket_setaside_notimpl
,
111 apr_bucket_shared_split
,
112 apr_bucket_shared_copy
115 static void lob_bucket_destroy(void *data
)
117 apr_bucket_lob
*f
= data
;
119 if (apr_bucket_shared_destroy(f
)) {
120 /* no need to destroy database objects here; it will get
121 * done automatically when the pool gets cleaned up */
126 static apr_status_t
lob_bucket_read(apr_bucket
*e
, const char **str
,
127 apr_size_t
*len
, apr_read_type_e block
)
129 apr_bucket_lob
*a
= e
->data
;
130 const apr_dbd_row_t
*row
= a
->row
;
131 apr_dbd_results_t
*res
= row
->res
;
133 apr_bucket
*b
= NULL
;
135 apr_size_t blength
= e
->length
; /* bytes remaining in file past offset */
136 apr_off_t boffset
= e
->start
;
137 MYSQL_BIND
*bind
= &res
->bind
[col
];
139 *str
= NULL
; /* in case we die prematurely */
141 /* fetch from offset if not at the beginning */
143 rv
= mysql_stmt_fetch_column(res
->statement
, bind
, col
,
144 (unsigned long) boffset
);
149 blength
-= blength
> bind
->buffer_length
? bind
->buffer_length
: blength
;
150 *len
= e
->length
- blength
;
153 /* allocate new buffer, since we used this one for the bucket */
154 bind
->buffer
= apr_palloc(res
->pool
, bind
->buffer_length
);
157 * Change the current bucket to refer to what we read,
158 * even if we read nothing because we hit EOF.
160 apr_bucket_pool_make(e
, *str
, *len
, res
->pool
);
162 /* If we have more to read from the field, then create another bucket */
164 /* for efficiency, we can just build a new apr_bucket struct
165 * to wrap around the existing LOB bucket */
166 b
= apr_bucket_alloc(sizeof(*b
), e
->list
);
167 b
->start
= boffset
+ *len
;
170 b
->type
= &apr_bucket_type_lob
;
171 b
->free
= apr_bucket_free
;
173 APR_BUCKET_INSERT_AFTER(e
, b
);
176 lob_bucket_destroy(a
);
182 static apr_bucket
*apr_bucket_lob_make(apr_bucket
*b
,
183 const apr_dbd_row_t
*row
, int col
,
184 apr_off_t offset
, apr_size_t len
,
189 f
= apr_bucket_alloc(sizeof(*f
), b
->list
);
194 b
= apr_bucket_shared_make(b
, f
, offset
, len
);
195 b
->type
= &apr_bucket_type_lob
;
200 static apr_bucket
*apr_bucket_lob_create(const apr_dbd_row_t
*row
, int col
,
202 apr_size_t len
, apr_pool_t
*p
,
203 apr_bucket_alloc_t
*list
)
205 apr_bucket
*b
= apr_bucket_alloc(sizeof(*b
), list
);
208 b
->free
= apr_bucket_free
;
210 return apr_bucket_lob_make(b
, row
, col
, offset
, len
, p
);
213 static apr_status_t
free_result(void *data
)
215 mysql_free_result(data
);
219 static int dbd_mysql_select(apr_pool_t
*pool
, apr_dbd_t
*sql
,
220 apr_dbd_results_t
**results
,
221 const char *query
, int seek
)
225 if (sql
->trans
&& sql
->trans
->errnum
) {
226 return sql
->trans
->errnum
;
228 ret
= mysql_query(sql
->conn
, query
);
230 if (sz
= mysql_field_count(sql
->conn
), sz
> 0) {
232 *results
= apr_palloc(pool
, sizeof(apr_dbd_results_t
));
234 (*results
)->random
= seek
;
235 (*results
)->statement
= NULL
;
236 (*results
)->pool
= pool
;
238 (*results
)->res
= mysql_store_result(sql
->conn
);
241 (*results
)->res
= mysql_use_result(sql
->conn
);
243 apr_pool_cleanup_register(pool
, (*results
)->res
,
244 free_result
,apr_pool_cleanup_null
);
247 ret
= mysql_errno(sql
->conn
);
250 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
251 sql
->trans
->errnum
= ret
;
256 static const char *dbd_mysql_get_name(const apr_dbd_results_t
*res
, int n
)
258 if ((n
< 0) || (n
>= (int) mysql_num_fields(res
->res
))) {
262 return mysql_fetch_fields(res
->res
)[n
].name
;
265 static int dbd_mysql_get_row(apr_pool_t
*pool
, apr_dbd_results_t
*res
,
266 apr_dbd_row_t
**row
, int rownum
)
271 if (res
->statement
) {
274 mysql_stmt_data_seek(res
->statement
, (my_ulonglong
) --rownum
);
277 return -1; /* invalid row */
280 ret
= mysql_stmt_fetch(res
->statement
);
283 ret
= mysql_stmt_errno(res
->statement
);
289 ret
= 0; /* bad luck - get_entry will deal with this */
296 mysql_data_seek(res
->res
, (my_ulonglong
) --rownum
);
299 return -1; /* invalid row */
302 r
= mysql_fetch_row(res
->res
);
309 *row
= apr_palloc(pool
, sizeof(apr_dbd_row_t
));
313 (*row
)->len
= mysql_fetch_lengths(res
->res
);
316 apr_pool_cleanup_run(pool
, res
->res
, free_result
);
321 /* An improved API that was proposed but not followed up */
322 static int dbd_mysql_get_entry(const apr_dbd_row_t
*row
, int n
,
323 apr_dbd_datum_t
*val
)
326 if (row
->res
->statement
) {
327 bind
= &row
->res
->bind
[n
];
328 if (mysql_stmt_fetch_column(row
->res
->statement
, bind
, n
, 0) != 0) {
329 val
->type
= APR_DBD_VALUE_NULL
;
332 if (*bind
->is_null
) {
333 val
->type
= APR_DBD_VALUE_NULL
;
337 val
->type
= APR_DBD_VALUE_STRING
;
338 val
->value
.stringval
= bind
->buffer
;
342 val
->type
= APR_DBD_VALUE_STRING
;
343 val
->value
.stringval
= row
->row
[n
];
349 static const char *dbd_mysql_get_entry(const apr_dbd_row_t
*row
, int n
)
352 if (row
->res
->statement
) {
353 bind
= &row
->res
->bind
[n
];
354 if (mysql_stmt_fetch_column(row
->res
->statement
, bind
, n
, 0) != 0) {
357 if (*bind
->is_null
) {
371 static apr_status_t
dbd_mysql_datum_get(const apr_dbd_row_t
*row
, int n
,
372 apr_dbd_type_e type
, void *data
)
374 if (row
->res
->statement
) {
375 MYSQL_BIND
*bind
= &row
->res
->bind
[n
];
376 unsigned long len
= *bind
->length
;
378 if (mysql_stmt_fetch_column(row
->res
->statement
, bind
, n
, 0) != 0) {
382 if (*bind
->is_null
) {
387 case APR_DBD_TYPE_TINY
:
388 *(char*)data
= atoi(bind
->buffer
);
390 case APR_DBD_TYPE_UTINY
:
391 *(unsigned char*)data
= atoi(bind
->buffer
);
393 case APR_DBD_TYPE_SHORT
:
394 *(short*)data
= atoi(bind
->buffer
);
396 case APR_DBD_TYPE_USHORT
:
397 *(unsigned short*)data
= atoi(bind
->buffer
);
399 case APR_DBD_TYPE_INT
:
400 *(int*)data
= atoi(bind
->buffer
);
402 case APR_DBD_TYPE_UINT
:
403 *(unsigned int*)data
= atoi(bind
->buffer
);
405 case APR_DBD_TYPE_LONG
:
406 *(long*)data
= atol(bind
->buffer
);
408 case APR_DBD_TYPE_ULONG
:
409 *(unsigned long*)data
= atol(bind
->buffer
);
411 case APR_DBD_TYPE_LONGLONG
:
412 *(apr_int64_t
*)data
= apr_atoi64(bind
->buffer
);
414 case APR_DBD_TYPE_ULONGLONG
:
415 *(apr_uint64_t
*)data
= apr_atoi64(bind
->buffer
);
417 case APR_DBD_TYPE_FLOAT
:
418 *(float*)data
= (float) atof(bind
->buffer
);
420 case APR_DBD_TYPE_DOUBLE
:
421 *(double*)data
= atof(bind
->buffer
);
423 case APR_DBD_TYPE_STRING
:
424 case APR_DBD_TYPE_TEXT
:
425 case APR_DBD_TYPE_TIME
:
426 case APR_DBD_TYPE_DATE
:
427 case APR_DBD_TYPE_DATETIME
:
428 case APR_DBD_TYPE_TIMESTAMP
:
429 case APR_DBD_TYPE_ZTIMESTAMP
:
430 *((char*)bind
->buffer
+bind
->buffer_length
-1) = '\0';
431 *(char**)data
= bind
->buffer
;
433 case APR_DBD_TYPE_BLOB
:
434 case APR_DBD_TYPE_CLOB
:
437 apr_bucket_brigade
*b
= (apr_bucket_brigade
*)data
;
439 e
= apr_bucket_lob_create(row
, n
, 0, len
,
440 row
->res
->pool
, b
->bucket_alloc
);
441 APR_BRIGADE_INSERT_TAIL(b
, e
);
444 case APR_DBD_TYPE_NULL
:
445 *(void**)data
= NULL
;
452 if (row
->row
[n
] == NULL
) {
457 case APR_DBD_TYPE_TINY
:
458 *(char*)data
= atoi(row
->row
[n
]);
460 case APR_DBD_TYPE_UTINY
:
461 *(unsigned char*)data
= atoi(row
->row
[n
]);
463 case APR_DBD_TYPE_SHORT
:
464 *(short*)data
= atoi(row
->row
[n
]);
466 case APR_DBD_TYPE_USHORT
:
467 *(unsigned short*)data
= atoi(row
->row
[n
]);
469 case APR_DBD_TYPE_INT
:
470 *(int*)data
= atoi(row
->row
[n
]);
472 case APR_DBD_TYPE_UINT
:
473 *(unsigned int*)data
= atoi(row
->row
[n
]);
475 case APR_DBD_TYPE_LONG
:
476 *(long*)data
= atol(row
->row
[n
]);
478 case APR_DBD_TYPE_ULONG
:
479 *(unsigned long*)data
= atol(row
->row
[n
]);
481 case APR_DBD_TYPE_LONGLONG
:
482 *(apr_int64_t
*)data
= apr_atoi64(row
->row
[n
]);
484 case APR_DBD_TYPE_ULONGLONG
:
485 *(apr_uint64_t
*)data
= apr_atoi64(row
->row
[n
]);
487 case APR_DBD_TYPE_FLOAT
:
488 *(float*)data
= (float) atof(row
->row
[n
]);
490 case APR_DBD_TYPE_DOUBLE
:
491 *(double*)data
= atof(row
->row
[n
]);
493 case APR_DBD_TYPE_STRING
:
494 case APR_DBD_TYPE_TEXT
:
495 case APR_DBD_TYPE_TIME
:
496 case APR_DBD_TYPE_DATE
:
497 case APR_DBD_TYPE_DATETIME
:
498 case APR_DBD_TYPE_TIMESTAMP
:
499 case APR_DBD_TYPE_ZTIMESTAMP
:
500 *(char**)data
= row
->row
[n
];
502 case APR_DBD_TYPE_BLOB
:
503 case APR_DBD_TYPE_CLOB
:
506 apr_bucket_brigade
*b
= (apr_bucket_brigade
*)data
;
508 e
= apr_bucket_pool_create(row
->row
[n
], row
->len
[n
],
509 row
->res
->pool
, b
->bucket_alloc
);
510 APR_BRIGADE_INSERT_TAIL(b
, e
);
513 case APR_DBD_TYPE_NULL
:
514 *(void**)data
= NULL
;
523 static const char *dbd_mysql_error(apr_dbd_t
*sql
, int n
)
525 return mysql_error(sql
->conn
);
528 static int dbd_mysql_query(apr_dbd_t
*sql
, int *nrows
, const char *query
)
531 if (sql
->trans
&& sql
->trans
->errnum
) {
532 return sql
->trans
->errnum
;
534 ret
= mysql_query(sql
->conn
, query
);
536 ret
= mysql_errno(sql
->conn
);
538 *nrows
= (int) mysql_affected_rows(sql
->conn
);
539 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
540 sql
->trans
->errnum
= ret
;
545 static const char *dbd_mysql_escape(apr_pool_t
*pool
, const char *arg
,
548 unsigned long len
= strlen(arg
);
549 char *ret
= apr_palloc(pool
, 2*len
+ 1);
550 mysql_real_escape_string(sql
->conn
, ret
, arg
, len
);
554 static apr_status_t
stmt_close(void *data
)
556 mysql_stmt_close(data
);
560 static int dbd_mysql_prepare(apr_pool_t
*pool
, apr_dbd_t
*sql
,
561 const char *query
, const char *label
,
562 int nargs
, int nvals
, apr_dbd_type_e
*types
,
563 apr_dbd_prepared_t
**statement
)
565 /* Translate from apr_dbd to native query format */
569 *statement
= apr_palloc(pool
, sizeof(apr_dbd_prepared_t
));
571 (*statement
)->stmt
= mysql_stmt_init(sql
->conn
);
573 if ((*statement
)->stmt
) {
574 apr_pool_cleanup_register(pool
, (*statement
)->stmt
,
575 stmt_close
, apr_pool_cleanup_null
);
576 ret
= mysql_stmt_prepare((*statement
)->stmt
, query
, strlen(query
));
579 ret
= mysql_stmt_errno((*statement
)->stmt
);
582 (*statement
)->nargs
= nargs
;
583 (*statement
)->nvals
= nvals
;
584 (*statement
)->types
= types
;
589 return CR_OUT_OF_MEMORY
;
592 static void dbd_mysql_bind(apr_dbd_prepared_t
*statement
,
593 const char **values
, MYSQL_BIND
*bind
)
597 for (i
= 0, j
= 0; i
< statement
->nargs
; i
++, j
++) {
598 bind
[i
].length
= &bind
[i
].buffer_length
;
599 bind
[i
].is_unsigned
= 0;
600 bind
[i
].is_null
= NULL
;
602 if (values
[j
] == NULL
) {
603 bind
[i
].buffer_type
= MYSQL_TYPE_NULL
;
606 switch (statement
->types
[i
]) {
607 case APR_DBD_TYPE_BLOB
:
608 case APR_DBD_TYPE_CLOB
:
609 bind
[i
].buffer_type
= MYSQL_TYPE_LONG_BLOB
;
610 bind
[i
].buffer
= (void*)values
[j
];
611 bind
[i
].buffer_length
= atol(values
[++j
]);
613 /* skip table and column */
617 bind
[i
].buffer_type
= MYSQL_TYPE_VAR_STRING
;
618 bind
[i
].buffer
= (void*)values
[j
];
619 bind
[i
].buffer_length
= strlen(values
[j
]);
628 static int dbd_mysql_pquery_internal(apr_pool_t
*pool
, apr_dbd_t
*sql
,
629 int *nrows
, apr_dbd_prepared_t
*statement
,
634 ret
= mysql_stmt_bind_param(statement
->stmt
, bind
);
637 ret
= mysql_stmt_errno(statement
->stmt
);
640 ret
= mysql_stmt_execute(statement
->stmt
);
642 ret
= mysql_stmt_errno(statement
->stmt
);
644 *nrows
= (int) mysql_stmt_affected_rows(statement
->stmt
);
650 static int dbd_mysql_pquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
651 int *nrows
, apr_dbd_prepared_t
*statement
,
657 if (sql
->trans
&& sql
->trans
->errnum
) {
658 return sql
->trans
->errnum
;
661 bind
= apr_palloc(pool
, statement
->nargs
* sizeof(MYSQL_BIND
));
663 dbd_mysql_bind(statement
, values
, bind
);
665 ret
= dbd_mysql_pquery_internal(pool
, sql
, nrows
, statement
, bind
);
667 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
668 sql
->trans
->errnum
= ret
;
673 static int dbd_mysql_pvquery(apr_pool_t
*pool
, apr_dbd_t
*sql
, int *nrows
,
674 apr_dbd_prepared_t
*statement
, va_list args
)
679 if (sql
->trans
&& sql
->trans
->errnum
) {
680 return sql
->trans
->errnum
;
683 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nvals
);
685 for (i
= 0; i
< statement
->nvals
; i
++) {
686 values
[i
] = va_arg(args
, const char*);
689 return dbd_mysql_pquery(pool
, sql
, nrows
, statement
, values
);
692 static int dbd_mysql_pselect_internal(apr_pool_t
*pool
, apr_dbd_t
*sql
,
693 apr_dbd_results_t
**res
,
694 apr_dbd_prepared_t
*statement
,
695 int random
, MYSQL_BIND
*bind
)
699 #if MYSQL_VERSION_ID >= 50000
703 unsigned long *length
, maxlen
;
705 ret
= mysql_stmt_bind_param(statement
->stmt
, bind
);
707 ret
= mysql_stmt_execute(statement
->stmt
);
710 *res
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
712 (*res
)->random
= random
;
713 (*res
)->statement
= statement
->stmt
;
714 (*res
)->res
= mysql_stmt_result_metadata(statement
->stmt
);
716 apr_pool_cleanup_register(pool
, (*res
)->res
,
717 free_result
, apr_pool_cleanup_null
);
718 nfields
= mysql_num_fields((*res
)->res
);
720 (*res
)->bind
= apr_palloc(pool
, nfields
*sizeof(MYSQL_BIND
));
721 length
= apr_pcalloc(pool
, nfields
*sizeof(unsigned long));
722 #if MYSQL_VERSION_ID >= 50000
723 error
= apr_palloc(pool
, nfields
*sizeof(my_bool
));
725 is_nullr
= apr_pcalloc(pool
, nfields
*sizeof(my_bool
));
726 for ( i
= 0; i
< nfields
; ++i
) {
727 maxlen
= ((*res
)->res
->fields
[i
].length
< sql
->fldsz
?
728 (*res
)->res
->fields
[i
].length
: sql
->fldsz
) + 1;
729 if ((*res
)->res
->fields
[i
].type
== MYSQL_TYPE_BLOB
) {
730 (*res
)->bind
[i
].buffer_type
= MYSQL_TYPE_LONG_BLOB
;
733 (*res
)->bind
[i
].buffer_type
= MYSQL_TYPE_VAR_STRING
;
735 (*res
)->bind
[i
].buffer_length
= maxlen
;
736 (*res
)->bind
[i
].length
= &length
[i
];
737 (*res
)->bind
[i
].buffer
= apr_palloc(pool
, maxlen
);
738 (*res
)->bind
[i
].is_null
= is_nullr
+i
;
739 #if MYSQL_VERSION_ID >= 50000
740 (*res
)->bind
[i
].error
= error
+i
;
744 ret
= mysql_stmt_bind_result(statement
->stmt
, (*res
)->bind
);
746 ret
= mysql_stmt_store_result(statement
->stmt
);
751 ret
= mysql_stmt_errno(statement
->stmt
);
757 static int dbd_mysql_pselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
758 apr_dbd_results_t
**res
,
759 apr_dbd_prepared_t
*statement
, int random
,
765 if (sql
->trans
&& sql
->trans
->errnum
) {
766 return sql
->trans
->errnum
;
769 bind
= apr_palloc(pool
, statement
->nargs
* sizeof(MYSQL_BIND
));
771 dbd_mysql_bind(statement
, args
, bind
);
773 ret
= dbd_mysql_pselect_internal(pool
, sql
, res
, statement
, random
, bind
);
775 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
776 sql
->trans
->errnum
= ret
;
781 static int dbd_mysql_pvselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
782 apr_dbd_results_t
**res
,
783 apr_dbd_prepared_t
*statement
, int random
,
789 if (sql
->trans
&& sql
->trans
->errnum
) {
790 return sql
->trans
->errnum
;
793 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nvals
);
795 for (i
= 0; i
< statement
->nvals
; i
++) {
796 values
[i
] = va_arg(args
, const char*);
799 return dbd_mysql_pselect(pool
, sql
, res
, statement
, random
, values
);
802 static void dbd_mysql_bbind(apr_pool_t
*pool
, apr_dbd_prepared_t
*statement
,
803 const void **values
, MYSQL_BIND
*bind
)
809 for (i
= 0, j
= 0; i
< statement
->nargs
; i
++, j
++) {
810 arg
= (void *)values
[j
];
812 bind
[i
].length
= &bind
[i
].buffer_length
;
813 bind
[i
].is_null
= NULL
;
815 type
= (arg
== NULL
? APR_DBD_TYPE_NULL
: statement
->types
[i
]);
817 case APR_DBD_TYPE_TINY
:
818 bind
[i
].buffer
= arg
;
819 bind
[i
].buffer_type
= MYSQL_TYPE_TINY
;
820 bind
[i
].is_unsigned
= 0;
822 case APR_DBD_TYPE_UTINY
:
823 bind
[i
].buffer
= arg
;
824 bind
[i
].buffer_type
= MYSQL_TYPE_TINY
;
825 bind
[i
].is_unsigned
= 1;
827 case APR_DBD_TYPE_SHORT
:
828 bind
[i
].buffer
= arg
;
829 bind
[i
].buffer_type
= MYSQL_TYPE_SHORT
;
830 bind
[i
].is_unsigned
= 0;
832 case APR_DBD_TYPE_USHORT
:
833 bind
[i
].buffer
= arg
;
834 bind
[i
].buffer_type
= MYSQL_TYPE_SHORT
;
835 bind
[i
].is_unsigned
= 1;
837 case APR_DBD_TYPE_INT
:
838 bind
[i
].buffer
= arg
;
839 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
840 bind
[i
].is_unsigned
= 0;
842 case APR_DBD_TYPE_UINT
:
843 bind
[i
].buffer
= arg
;
844 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
845 bind
[i
].is_unsigned
= 1;
847 case APR_DBD_TYPE_LONG
:
848 if (sizeof(int) == sizeof(long)) {
849 bind
[i
].buffer
= arg
;
852 bind
[i
].buffer
= apr_palloc(pool
, sizeof(int));
853 *(int*)bind
[i
].buffer
= *(long*)arg
;
855 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
856 bind
[i
].is_unsigned
= 0;
858 case APR_DBD_TYPE_ULONG
:
859 if (sizeof(unsigned int) == sizeof(unsigned long)) {
860 bind
[i
].buffer
= arg
;
863 bind
[i
].buffer
= apr_palloc(pool
, sizeof(unsigned int));
864 *(unsigned int*)bind
[i
].buffer
= *(unsigned long*)arg
;
866 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
867 bind
[i
].is_unsigned
= 1;
869 case APR_DBD_TYPE_LONGLONG
:
870 if (sizeof(my_ulonglong
) == sizeof(apr_int64_t
)) {
871 bind
[i
].buffer
= arg
;
872 bind
[i
].buffer_type
= MYSQL_TYPE_LONGLONG
;
874 else { /* have to downsize, long long is not portable */
875 bind
[i
].buffer
= apr_palloc(pool
, sizeof(long));
876 *(long*)bind
[i
].buffer
= (long) *(apr_int64_t
*)arg
;
877 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
879 bind
[i
].is_unsigned
= 0;
881 case APR_DBD_TYPE_ULONGLONG
:
882 if (sizeof(my_ulonglong
) == sizeof(apr_uint64_t
)) {
883 bind
[i
].buffer
= arg
;
884 bind
[i
].buffer_type
= MYSQL_TYPE_LONGLONG
;
886 else { /* have to downsize, long long is not portable */
887 bind
[i
].buffer
= apr_palloc(pool
, sizeof(long));
888 *(unsigned long*)bind
[i
].buffer
=
889 (unsigned long) *(apr_uint64_t
*)arg
;
890 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
892 bind
[i
].is_unsigned
= 1;
894 case APR_DBD_TYPE_FLOAT
:
895 bind
[i
].buffer
= arg
;
896 bind
[i
].buffer_type
= MYSQL_TYPE_FLOAT
;
897 bind
[i
].is_unsigned
= 0;
899 case APR_DBD_TYPE_DOUBLE
:
900 bind
[i
].buffer
= arg
;
901 bind
[i
].buffer_type
= MYSQL_TYPE_DOUBLE
;
902 bind
[i
].is_unsigned
= 0;
904 case APR_DBD_TYPE_STRING
:
905 case APR_DBD_TYPE_TEXT
:
906 case APR_DBD_TYPE_TIME
:
907 case APR_DBD_TYPE_DATE
:
908 case APR_DBD_TYPE_DATETIME
:
909 case APR_DBD_TYPE_TIMESTAMP
:
910 case APR_DBD_TYPE_ZTIMESTAMP
:
911 bind
[i
].buffer
= arg
;
912 bind
[i
].buffer_type
= MYSQL_TYPE_VAR_STRING
;
913 bind
[i
].is_unsigned
= 0;
914 bind
[i
].buffer_length
= strlen((const char *)arg
);
916 case APR_DBD_TYPE_BLOB
:
917 case APR_DBD_TYPE_CLOB
:
918 bind
[i
].buffer
= (void *)arg
;
919 bind
[i
].buffer_type
= MYSQL_TYPE_LONG_BLOB
;
920 bind
[i
].is_unsigned
= 0;
921 bind
[i
].buffer_length
= *(apr_size_t
*)values
[++j
];
923 /* skip table and column */
926 case APR_DBD_TYPE_NULL
:
928 bind
[i
].buffer_type
= MYSQL_TYPE_NULL
;
936 static int dbd_mysql_pbquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
937 int *nrows
, apr_dbd_prepared_t
*statement
,
943 if (sql
->trans
&& sql
->trans
->errnum
) {
944 return sql
->trans
->errnum
;
947 bind
= apr_palloc(pool
, statement
->nargs
* sizeof(MYSQL_BIND
));
949 dbd_mysql_bbind(pool
, statement
, values
, bind
);
951 ret
= dbd_mysql_pquery_internal(pool
, sql
, nrows
, statement
, bind
);
953 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
954 sql
->trans
->errnum
= ret
;
959 static int dbd_mysql_pvbquery(apr_pool_t
*pool
, apr_dbd_t
*sql
, int *nrows
,
960 apr_dbd_prepared_t
*statement
, va_list args
)
965 if (sql
->trans
&& sql
->trans
->errnum
) {
966 return sql
->trans
->errnum
;
969 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nvals
);
971 for (i
= 0; i
< statement
->nvals
; i
++) {
972 values
[i
] = va_arg(args
, const void*);
975 return dbd_mysql_pbquery(pool
, sql
, nrows
, statement
, values
);
978 static int dbd_mysql_pbselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
979 apr_dbd_results_t
**res
,
980 apr_dbd_prepared_t
*statement
, int random
,
986 if (sql
->trans
&& sql
->trans
->errnum
) {
987 return sql
->trans
->errnum
;
990 bind
= apr_palloc(pool
, statement
->nargs
* sizeof(MYSQL_BIND
));
992 dbd_mysql_bbind(pool
, statement
, args
, bind
);
994 ret
= dbd_mysql_pselect_internal(pool
, sql
, res
, statement
, random
, bind
);
996 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
997 sql
->trans
->errnum
= ret
;
1002 static int dbd_mysql_pvbselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
1003 apr_dbd_results_t
**res
,
1004 apr_dbd_prepared_t
*statement
, int random
,
1007 const void **values
;
1010 if (sql
->trans
&& sql
->trans
->errnum
) {
1011 return sql
->trans
->errnum
;
1014 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nvals
);
1016 for (i
= 0; i
< statement
->nvals
; i
++) {
1017 values
[i
] = va_arg(args
, const void*);
1020 return dbd_mysql_pbselect(pool
, sql
, res
, statement
, random
, values
);
1023 static int dbd_mysql_end_transaction(apr_dbd_transaction_t
*trans
)
1027 /* rollback on error or explicit rollback request */
1028 if (trans
->errnum
|| TXN_DO_ROLLBACK(trans
)) {
1030 ret
= mysql_rollback(trans
->handle
->conn
);
1033 ret
= mysql_commit(trans
->handle
->conn
);
1036 ret
|= mysql_autocommit(trans
->handle
->conn
, 1);
1037 trans
->handle
->trans
= NULL
;
1040 /* Whether or not transactions work depends on whether the
1041 * underlying DB supports them within MySQL. Unfortunately
1042 * it fails silently with the default InnoDB.
1045 static int dbd_mysql_transaction(apr_pool_t
*pool
, apr_dbd_t
*handle
,
1046 apr_dbd_transaction_t
**trans
)
1048 /* Don't try recursive transactions here */
1049 if (handle
->trans
) {
1050 dbd_mysql_end_transaction(handle
->trans
) ;
1053 *trans
= apr_pcalloc(pool
, sizeof(apr_dbd_transaction_t
));
1055 (*trans
)->errnum
= mysql_autocommit(handle
->conn
, 0);
1056 (*trans
)->handle
= handle
;
1057 handle
->trans
= *trans
;
1058 return (*trans
)->errnum
;
1061 static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t
*trans
)
1064 return APR_DBD_TRANSACTION_COMMIT
;
1069 static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t
*trans
,
1073 return APR_DBD_TRANSACTION_COMMIT
;
1075 return trans
->mode
= (mode
& TXN_MODE_BITS
);
1078 static apr_dbd_t
*dbd_mysql_open(apr_pool_t
*pool
, const char *params
,
1081 static const char *const delims
= " \r\n\t;|,";
1088 #if MYSQL_VERSION_ID >= 50013
1089 my_bool do_reconnect
= 1;
1092 unsigned long flags
= 0;
1107 {"reconnect", NULL
},
1110 unsigned int port
= 0;
1111 apr_dbd_t
*sql
= apr_pcalloc(pool
, sizeof(apr_dbd_t
));
1112 sql
->fldsz
= FIELDSIZE
;
1113 sql
->conn
= mysql_init(sql
->conn
);
1114 if ( sql
->conn
== NULL
) {
1117 for (ptr
= strchr(params
, '='); ptr
; ptr
= strchr(ptr
, '=')) {
1118 /* don't dereference memory that may not belong to us */
1119 if (ptr
== params
) {
1123 for (key
= ptr
-1; apr_isspace(*key
); --key
);
1125 while (apr_isalpha(*key
)) {
1126 /* don't parse backwards off the start of the string */
1127 if (key
== params
) {
1136 for (value
= ptr
+1; apr_isspace(*value
); ++value
);
1137 vlen
= strcspn(value
, delims
);
1138 for (i
= 0; fields
[i
].field
!= NULL
; i
++) {
1139 if (!strncasecmp(fields
[i
].field
, key
, klen
)) {
1140 fields
[i
].value
= apr_pstrndup(pool
, value
, vlen
);
1146 if (fields
[4].value
!= NULL
) {
1147 port
= atoi(fields
[4].value
);
1149 if (fields
[6].value
!= NULL
&&
1150 !strcmp(fields
[6].value
, "CLIENT_FOUND_ROWS")) {
1151 flags
|= CLIENT_FOUND_ROWS
; /* only option we know */
1153 if (fields
[7].value
!= NULL
) {
1154 sql
->fldsz
= atol(fields
[7].value
);
1156 if (fields
[8].value
!= NULL
) {
1157 mysql_options(sql
->conn
, MYSQL_READ_DEFAULT_GROUP
, fields
[8].value
);
1159 #if MYSQL_VERSION_ID >= 50013
1160 if (fields
[9].value
!= NULL
) {
1161 do_reconnect
= atoi(fields
[9].value
) ? 1 : 0;
1165 #if MYSQL_VERSION_ID >= 50013
1166 /* the MySQL manual says this should be BEFORE mysql_real_connect */
1167 mysql_options(sql
->conn
, MYSQL_OPT_RECONNECT
, &do_reconnect
);
1170 real_conn
= mysql_real_connect(sql
->conn
, fields
[0].value
,
1171 fields
[1].value
, fields
[2].value
,
1172 fields
[3].value
, port
,
1173 fields
[5].value
, flags
);
1175 if(real_conn
== NULL
) {
1177 *error
= apr_pstrdup(pool
, mysql_error(sql
->conn
));
1179 mysql_close(sql
->conn
);
1183 #if MYSQL_VERSION_ID >= 50013
1184 /* Some say this should be AFTER mysql_real_connect */
1185 mysql_options(sql
->conn
, MYSQL_OPT_RECONNECT
, &do_reconnect
);
1191 static apr_status_t
dbd_mysql_close(apr_dbd_t
*handle
)
1193 mysql_close(handle
->conn
);
1197 static apr_status_t
dbd_mysql_check_conn(apr_pool_t
*pool
,
1200 return mysql_ping(handle
->conn
) ? APR_EGENERAL
: APR_SUCCESS
;
1203 static int dbd_mysql_select_db(apr_pool_t
*pool
, apr_dbd_t
* handle
,
1206 return mysql_select_db(handle
->conn
, name
);
1209 static void *dbd_mysql_native(apr_dbd_t
*handle
)
1211 return handle
->conn
;
1214 static int dbd_mysql_num_cols(apr_dbd_results_t
*res
)
1216 if (res
->statement
) {
1217 return mysql_stmt_field_count(res
->statement
);
1220 return mysql_num_fields(res
->res
);
1224 static int dbd_mysql_num_tuples(apr_dbd_results_t
*res
)
1227 if (res
->statement
) {
1228 return (int) mysql_stmt_num_rows(res
->statement
);
1231 return (int) mysql_num_rows(res
->res
);
1239 static apr_status_t
thread_end(void *data
)
1245 static void dbd_mysql_init(apr_pool_t
*pool
)
1248 mysql_thread_init();
1250 /* FIXME: this is a guess; find out what it really does */
1251 apr_pool_cleanup_register(pool
, NULL
, thread_end
, apr_pool_cleanup_null
);
1253 APU_MODULE_DECLARE_DATA
const apr_dbd_driver_t apr_dbd_mysql_driver
= {
1258 dbd_mysql_check_conn
,
1260 dbd_mysql_select_db
,
1261 dbd_mysql_transaction
,
1262 dbd_mysql_end_transaction
,
1266 dbd_mysql_num_tuples
,
1268 dbd_mysql_get_entry
,
1277 dbd_mysql_transaction_mode_get
,
1278 dbd_mysql_transaction_mode_set
,
1281 dbd_mysql_pvbselect
,