On Tue, 2006-08-15 at 06:58 +1000, Bojan Smojver wrote:
> PS. Due to size issues on the list, I'll send the MySQL patch in a
> separate e-mail.
MySQL patch.
--
Bojan
Index: apr_dbd_mysql.c
===================================================================
--- apr_dbd_mysql.c (revision 51)
+++ apr_dbd_mysql.c (working copy)
@@ -54,6 +54,7 @@
#endif
#include "apr_strings.h"
+#include "apr_buckets.h"
#include "apr_dbd_internal.h"
@@ -62,6 +63,8 @@
struct apr_dbd_prepared_t {
MYSQL_STMT* stmt;
+ int nargs;
+ apr_dbd_type_e *types;
};
struct apr_dbd_transaction_t {
@@ -87,8 +90,151 @@
struct apr_dbd_row_t {
MYSQL_ROW row;
apr_dbd_results_t *res;
+ unsigned long *len;
};
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+/* MySQL specific bucket for BLOB types */
+typedef struct apr_bucket_blob apr_bucket_blob;
+/**
+ * A bucket referring to a MySQL BLOB
+ */
+struct apr_bucket_blob {
+ /** Number of buckets using this memory */
+ apr_bucket_refcount refcount;
+ /** The row this bucket refers to */
+ const apr_dbd_row_t *row;
+ /** The column this bucket refers to */
+ int col;
+ /** The pool into which any needed structures should
+ * be created while reading from this bucket */
+ apr_pool_t *readpool;
+};
+
+static void blob_bucket_destroy(void *data);
+static apr_status_t blob_bucket_read(apr_bucket *e, const char **str,
+ apr_size_t *len, apr_read_type_e block);
+static apr_bucket *apr_bucket_blob_make(apr_bucket *b,
+ const apr_dbd_row_t *row, int col,
+ apr_off_t offset, apr_size_t len,
+ apr_pool_t *p);
+static apr_bucket *apr_bucket_blob_create(const apr_dbd_row_t *row, int col,
+ apr_off_t offset,
+ apr_size_t len, apr_pool_t *p,
+ apr_bucket_alloc_t *list);
+
+const apr_bucket_type_t apr_bucket_type_blob = {
+ "BLOB", 5, APR_BUCKET_DATA,
+ blob_bucket_destroy,
+ blob_bucket_read,
+ apr_bucket_setaside_notimpl,
+ apr_bucket_shared_split,
+ apr_bucket_shared_copy
+};
+
+static void blob_bucket_destroy(void *data)
+{
+ apr_bucket_blob *f = data;
+
+ if (apr_bucket_shared_destroy(f)) {
+ /* no need to destroy database objects here; it will get
+ * done automatically when the pool gets cleaned up */
+ apr_bucket_free(f);
+ }
+}
+
+static apr_status_t blob_bucket_read(apr_bucket *e, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ apr_bucket_blob *a = e->data;
+ const apr_dbd_row_t *row = a->row;
+ apr_dbd_results_t *res = row->res;
+ int col = a->col;
+ apr_bucket *b = NULL;
+ char *buf;
+ int rv;
+ apr_size_t blength = e->length; /* bytes remaining in file past offset */
+ apr_off_t boffset = e->start;
+ MYSQL_BIND *bind = &res->bind[col];
+
+ *str = NULL; /* in case we die prematurely */
+
+ bind->buffer_type = MYSQL_TYPE_BLOB;
+ /* allocate new buffer: not first fetch or buffer size too small */
+ if (boffset > 0 || bind->buffer_length < APR_BUCKET_BUFF_SIZE) {
+ bind->buffer_length = APR_BUCKET_BUFF_SIZE;
+ bind->buffer = apr_bucket_alloc(APR_BUCKET_BUFF_SIZE, e->list);
+ }
+
+ /* Fetch from offset... */
+ rv = mysql_stmt_fetch_column(res->statement, bind, col, boffset);
+ if (rv != 0) {
+ apr_bucket_free(buf);
+ return APR_EGENERAL;
+ }
+ blength -= *bind->length;
+ *len = *bind->length;
+ memcpy((void *)(*str), bind->buffer, *len);
+
+ /*
+ * Change the current bucket to refer to what we read,
+ * even if we read nothing because we hit EOF.
+ */
+ apr_bucket_heap_make(e, *str, *len, apr_bucket_free);
+
+ /* If we have more to read from the field, then create another bucket */
+ if (blength > 0 && rv != CR_NO_DATA) {
+ /* for efficiency, we can just build a new apr_bucket struct
+ * to wrap around the existing blob bucket */
+ b = apr_bucket_alloc(sizeof(*b), e->list);
+ b->start = boffset + *bind->length;
+ b->length = blength;
+ b->data = a;
+ b->type = &apr_bucket_type_blob;
+ b->free = apr_bucket_free;
+ b->list = e->list;
+ APR_BUCKET_INSERT_AFTER(e, b);
+ }
+ else {
+ blob_bucket_destroy(a);
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_bucket *apr_bucket_blob_make(apr_bucket *b,
+ const apr_dbd_row_t *row, int col,
+ apr_off_t offset, apr_size_t len,
+ apr_pool_t *p)
+{
+ apr_bucket_blob *f;
+
+ f = apr_bucket_alloc(sizeof(*f), b->list);
+ f->row = row;
+ f->col = col;
+ f->readpool = p;
+
+ b = apr_bucket_shared_make(b, f, offset, len);
+ b->type = &apr_bucket_type_blob;
+
+ return b;
+}
+
+static apr_bucket *apr_bucket_blob_create(const apr_dbd_row_t *row, int col,
+ apr_off_t offset,
+ apr_size_t len, apr_pool_t *p,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_blob_make(b, row, col, offset, len, p);
+}
+
+#endif
+
static apr_status_t free_result(void *data)
{
mysql_free_result(data);
@@ -145,6 +291,7 @@
return mysql_fetch_fields(res->res)[n].name;
}
#endif
+
static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
apr_dbd_row_t **row, int rownum)
{
@@ -186,6 +333,7 @@
*row = apr_palloc(pool, sizeof(apr_dbd_row_t));
}
(*row)->row = r;
+ (*row)->len = mysql_fetch_lengths(res->res);
(*row)->res = res;
}
else {
@@ -221,6 +369,7 @@
return 0;
}
#else
+
static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
{
MYSQL_BIND *bind;
@@ -242,10 +391,162 @@
return 0;
}
#endif
+
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n,
+ apr_dbd_type_e type, void *data)
+{
+ if (row->res->statement) {
+ MYSQL_BIND *bind = &row->res->bind[n];
+ unsigned long len = row->len[n];
+
+ if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
+ return APR_EGENERAL;
+ }
+
+ if (*bind->is_null) {
+ return APR_ENOENT;
+ }
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ *(char*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ *(unsigned char*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ *(short*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ *(unsigned short*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_INT:
+ *(int*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_UINT:
+ *(unsigned int*)data = atoi(bind->buffer);
+ break;
+ case APR_DBD_TYPE_LONG:
+ *(long*)data = atol(bind->buffer);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ *(unsigned long*)data = atol(bind->buffer);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ *(apr_int64_t*)data = apr_atoi64(bind->buffer);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ *(apr_uint64_t*)data = apr_atoi64(bind->buffer);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ *(float*)data = atof(bind->buffer);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ *(double*)data = atof(bind->buffer);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ *(char**)data = bind->buffer;
+ break;
+ case APR_DBD_TYPE_BLOB:
+ {
+ apr_bucket *e;
+ apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+ e = apr_bucket_blob_create(row, n, 0, len, b->p, b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ *(void**)data = NULL;
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+ }
+ else {
+ if (row->row[n] == NULL) {
+ return APR_ENOENT;
+ }
+
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ *(char*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_UTINY:
+ *(unsigned char*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_SHORT:
+ *(short*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_USHORT:
+ *(unsigned short*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_INT:
+ *(int*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_UINT:
+ *(unsigned int*)data = atoi(row->row[n]);
+ break;
+ case APR_DBD_TYPE_LONG:
+ *(long*)data = atol(row->row[n]);
+ break;
+ case APR_DBD_TYPE_ULONG:
+ *(unsigned long*)data = atol(row->row[n]);
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ *(apr_int64_t*)data = apr_atoi64(row->row[n]);
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ *(apr_uint64_t*)data = apr_atoi64(row->row[n]);
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ *(float*)data = atof(row->row[n]);
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ *(double*)data = atof(row->row[n]);
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ *(char**)data = row->row[n];
+ break;
+ case APR_DBD_TYPE_BLOB:
+ {
+ apr_bucket *e;
+ apr_bucket_brigade *b = (apr_bucket_brigade*)data;
+
+ e = apr_bucket_immortal_create(row->row[n], row->len[n],
+ b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ *(void**)data = NULL;
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+ }
+ return 0;
+}
+#endif
+
static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
{
return mysql_error(sql->conn);
}
+
static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query)
{
int ret;
@@ -266,6 +567,7 @@
}
return ret;
}
+
static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
apr_dbd_t *sql)
{
@@ -274,39 +576,21 @@
mysql_real_escape_string(sql->conn, ret, arg, len);
return ret;
}
+
static apr_status_t stmt_close(void *data)
{
mysql_stmt_close(data);
return APR_SUCCESS;
}
+
static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
const char *query, const char *label,
+ int nargs, apr_dbd_type_e *types,
apr_dbd_prepared_t **statement)
{
/* Translate from apr_dbd to native query format */
- char *myquery = apr_pstrdup(pool, query);
- char *p = myquery;
- const char *q;
int ret;
- for (q = query; *q; ++q) {
- if (q[0] == '%') {
- if (isalpha(q[1])) {
- *p++ = '?';
- ++q;
- }
- else if (q[1] == '%') {
- /* reduce %% to % */
- *p++ = *q++;
- }
- else {
- *p++ = *q;
- }
- }
- else {
- *p++ = *q;
- }
- }
- *p = 0;
+
if (!*statement) {
*statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
}
@@ -315,43 +599,66 @@
if ((*statement)->stmt) {
apr_pool_cleanup_register(pool, (*statement)->stmt,
stmt_close, apr_pool_cleanup_null);
- ret = mysql_stmt_prepare((*statement)->stmt, myquery, strlen(myquery));
+ ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query));
if (ret != 0) {
ret = mysql_stmt_errno((*statement)->stmt);
}
+ (*statement)->nargs = nargs;
+ (*statement)->types = types;
+
return ret;
}
return CR_OUT_OF_MEMORY;
}
-static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
- int *nrows, apr_dbd_prepared_t *statement,
- int nargs, const char **values)
+
+static void dbd_mysql_bind(apr_dbd_prepared_t *statement,
+ int nargs, const char **values, MYSQL_BIND *bind)
{
- MYSQL_BIND *bind;
- char *arg;
- int ret;
- int i;
+ int i, j;
my_bool is_null = FALSE;
- if (sql->trans && sql->trans->errnum) {
- return sql->trans->errnum;
- }
- nargs = mysql_stmt_param_count(statement->stmt);
-
- bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
- for (i=0; i < nargs; ++i) {
- arg = (char*)values[i];
- bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
- bind[i].buffer = arg;
- bind[i].buffer_length = strlen(arg);
+ for (i = 0; i < nargs; i++) {
bind[i].length = &bind[i].buffer_length;
bind[i].is_null = &is_null;
bind[i].is_unsigned = 0;
+
+ if (values[i] == NULL) {
+ bind[i].buffer_type = MYSQL_TYPE_NULL;
+ }
+ else {
+ if (statement->types[i] == APR_DBD_TYPE_BLOB) {
+ char *data = (char*)values[i];
+
+ /* encoding: length:table:column:payload */
+ for (j = 0; j < 3; j++) {
+ data = strchr(data, ':');
+ }
+ data++;
+
+ bind[i].buffer_type = MYSQL_TYPE_BLOB;
+ bind[i].buffer = data;
+ bind[i].buffer_length = atoi(values[i]);
+ }
+ else {
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].buffer = (void*)values[i];
+ bind[i].buffer_length = strlen(values[i]);
+ }
+ }
}
+ return;
+}
+
+static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ MYSQL_BIND *bind)
+{
+ int ret;
+
ret = mysql_stmt_bind_param(statement->stmt, bind);
if (ret != 0) {
*nrows = 0;
@@ -364,53 +671,27 @@
}
*nrows = mysql_stmt_affected_rows(statement->stmt);
}
-#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
- if (TXN_NOTICE_ERRORS(sql->trans)) {
-#else
- if (sql->trans) {
-#endif
- sql->trans->errnum = ret;
- }
+
return ret;
}
-static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
- apr_dbd_prepared_t *statement, va_list args)
+
+static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ int nargs, const char **values)
{
MYSQL_BIND *bind;
- char *arg;
int ret;
- int nargs = 0;
- int i;
- my_bool is_null = FALSE;
if (sql->trans && sql->trans->errnum) {
return sql->trans->errnum;
}
- nargs = mysql_stmt_param_count(statement->stmt);
- bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
- for (i=0; i < nargs; ++i) {
- arg = va_arg(args, char*);
- bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
- bind[i].buffer = arg;
- bind[i].buffer_length = strlen(arg);
- bind[i].length = &bind[i].buffer_length;
- bind[i].is_null = &is_null;
- bind[i].is_unsigned = 0;
- }
+ bind = apr_palloc(pool, nargs * sizeof(MYSQL_BIND));
- ret = mysql_stmt_bind_param(statement->stmt, bind);
- if (ret != 0) {
- *nrows = 0;
- ret = mysql_stmt_errno(statement->stmt);
- }
- else {
- ret = mysql_stmt_execute(statement->stmt);
- if (ret != 0) {
- ret = mysql_stmt_errno(statement->stmt);
- }
- *nrows = mysql_stmt_affected_rows(statement->stmt);
- }
+ dbd_mysql_bind(statement, nargs, values, bind);
+
+ ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
+
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
if (TXN_NOTICE_ERRORS(sql->trans)) {
#else
@@ -420,40 +701,40 @@
}
return ret;
}
-static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
- apr_dbd_results_t **res,
- apr_dbd_prepared_t *statement, int random,
- int nargs, const char **args)
+
+static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+ apr_dbd_prepared_t *statement, va_list args)
{
+ const char **values;
int i;
- int nfields;
- char *arg;
- my_bool is_null = FALSE;
- my_bool *is_nullr;
-#if MYSQL_VERSION_ID >= 50000
- my_bool *error;
-#endif
- int ret;
- unsigned long *length, maxlen;
- MYSQL_BIND *bind;
if (sql->trans && sql->trans->errnum) {
return sql->trans->errnum;
}
- nargs = mysql_stmt_param_count(statement->stmt);
- bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+ values = apr_palloc(pool, sizeof(*values) * statement->nargs);
- for (i=0; i < nargs; ++i) {
- arg = (char*)args[i];
- bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
- bind[i].buffer = arg;
- bind[i].buffer_length = strlen(arg);
- bind[i].length = &bind[i].buffer_length;
- bind[i].is_null = &is_null;
- bind[i].is_unsigned = 0;
+ for (i = 0; i < statement->nargs; i++) {
+ values[i] = va_arg(args, const char*);
}
+ return dbd_mysql_pquery(pool, sql, nrows, statement,
+ statement->nargs, values);
+}
+
+static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement,
+ int random, MYSQL_BIND *bind)
+{
+ int nfields, i;
+ my_bool *is_nullr;
+#if MYSQL_VERSION_ID >= 50000
+ my_bool *error;
+#endif
+ int ret;
+ unsigned long *length, maxlen;
+
ret = mysql_stmt_bind_param(statement->stmt, bind);
if (ret == 0) {
ret = mysql_stmt_execute(statement->stmt);
@@ -496,6 +777,28 @@
if (ret != 0) {
ret = mysql_stmt_errno(statement->stmt);
}
+
+ return ret;
+}
+
+static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement, int random,
+ int nargs, const char **args)
+{
+ int ret;
+ MYSQL_BIND *bind;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ bind = apr_palloc(pool, nargs * sizeof(MYSQL_BIND));
+
+ dbd_mysql_bind(statement, nargs, args, bind);
+
+ ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
+
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
if (TXN_NOTICE_ERRORS(sql->trans)) {
#else
@@ -505,83 +808,178 @@
}
return ret;
}
+
static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
apr_dbd_results_t **res,
apr_dbd_prepared_t *statement, int random,
va_list args)
{
+ const char **values;
int i;
- int nfields;
- char *arg;
- my_bool is_null = FALSE;
- my_bool *is_nullr;
-#if MYSQL_VERSION_ID >= 50000
- my_bool *error;
-#endif
- int ret;
- unsigned long *length, maxlen;
- int nargs;
- MYSQL_BIND *bind;
if (sql->trans && sql->trans->errnum) {
return sql->trans->errnum;
}
- nargs = mysql_stmt_param_count(statement->stmt);
- bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
+ values = apr_palloc(pool, sizeof(*values) * statement->nargs);
- for (i=0; i < nargs; ++i) {
- arg = va_arg(args, char*);
- bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
- bind[i].buffer = arg;
- bind[i].buffer_length = strlen(arg);
+ for (i = 0; i < statement->nargs; i++) {
+ values[i] = va_arg(args, const char*);
+ }
+
+ return dbd_mysql_pselect(pool, sql, res, statement, random,
+ statement->nargs, values);
+}
+
+static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement,
+ int nargs, const void **values, MYSQL_BIND *bind)
+{
+ void *arg;
+ int i;
+ my_bool is_null = FALSE;
+ apr_dbd_type_e type;
+
+ for (i = 0; i < nargs; i++) {
+ arg = (void *)values[i];
+
bind[i].length = &bind[i].buffer_length;
bind[i].is_null = &is_null;
- bind[i].is_unsigned = 0;
- }
- ret = mysql_stmt_bind_param(statement->stmt, bind);
- if (ret == 0) {
- ret = mysql_stmt_execute(statement->stmt);
- if (!ret) {
- if (!*res) {
- *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ type = (values[i] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
+ switch (type) {
+ case APR_DBD_TYPE_TINY:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_TINY;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_UTINY:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_TINY;
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_SHORT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_SHORT;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_USHORT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_SHORT;
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_INT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_UINT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_LONG:
+ if (sizeof(int) == sizeof(long)) {
+ bind[i].buffer = arg;
}
- (*res)->random = random;
- (*res)->statement = statement->stmt;
- (*res)->res = mysql_stmt_result_metadata(statement->stmt);
- apr_pool_cleanup_register(pool, (*res)->res,
- free_result, apr_pool_cleanup_null);
- nfields = mysql_num_fields((*res)->res);
- if (!(*res)->bind) {
- (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
- length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
-#if MYSQL_VERSION_ID >= 50000
- error = apr_palloc(pool, nfields*sizeof(my_bool));
-#endif
- is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
- for ( i = 0; i < nfields; ++i ) {
- maxlen = (*res)->res->fields[i].length < sql->fldsz ?
- (*res)->res->fields[i].length : sql->fldsz;
- (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
- (*res)->bind[i].buffer_length = maxlen;
- (*res)->bind[i].length = &length[i];
- (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
- (*res)->bind[i].is_null = is_nullr+i;
-#if MYSQL_VERSION_ID >= 50000
- (*res)->bind[i].error = error+i;
-#endif
- }
+ else {
+ bind[i].buffer = apr_palloc(pool, sizeof(int));
+ *(int*)bind[i].buffer = *(long*)arg;
}
- ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
- if (!ret) {
- ret = mysql_stmt_store_result(statement->stmt);
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_ULONG:
+ if (sizeof(unsigned int) == sizeof(unsigned long)) {
+ bind[i].buffer = arg;
}
+ else {
+ bind[i].buffer = apr_palloc(pool, sizeof(unsigned int));
+ *(unsigned int*)bind[i].buffer = *(unsigned long*)arg;
+ }
+ bind[i].buffer_type = MYSQL_TYPE_LONG;
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_LONGLONG:
+ if (sizeof(long long) == sizeof(apr_int64_t)) {
+ bind[i].buffer = arg;
+ }
+ else {
+ bind[i].buffer = apr_palloc(pool, sizeof(long long));
+ *(long long*)bind[i].buffer = *(apr_int64_t*)arg;
+ }
+ bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_ULONGLONG:
+ if (sizeof(unsigned long long) == sizeof(apr_uint64_t)) {
+ bind[i].buffer = arg;
+ }
+ else {
+ bind[i].buffer = apr_palloc(pool, sizeof(unsigned long long));
+ *(unsigned long long*)bind[i].buffer = *(apr_uint64_t*)arg;
+ }
+ bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
+ bind[i].is_unsigned = 1;
+ break;
+ case APR_DBD_TYPE_FLOAT:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_FLOAT;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_DOUBLE:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
+ bind[i].is_unsigned = 0;
+ break;
+ case APR_DBD_TYPE_STRING:
+ case APR_DBD_TYPE_TEXT:
+ case APR_DBD_TYPE_TIME:
+ case APR_DBD_TYPE_DATE:
+ case APR_DBD_TYPE_DATETIME:
+ case APR_DBD_TYPE_TIMESTAMP:
+ case APR_DBD_TYPE_ZTIMESTAMP:
+ bind[i].buffer = arg;
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].is_unsigned = 0;
+ bind[i].buffer_length = strlen((const char *)arg);
+ break;
+ case APR_DBD_TYPE_BLOB:
+ {
+ apr_dbd_blob_t *blob = (void *)arg;
+
+ bind[i].buffer = (void *)blob->data;
+ bind[i].buffer_type = MYSQL_TYPE_BLOB;
+ bind[i].is_unsigned = 0;
+ bind[i].buffer_length = blob->size;
+ }
+ break;
+ case APR_DBD_TYPE_NULL:
+ default:
+ bind[i].buffer_type = MYSQL_TYPE_NULL;
+ break;
}
}
- if (ret != 0) {
- ret = mysql_stmt_errno(statement->stmt);
+
+ return;
+}
+
+static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql,
+ int *nrows, apr_dbd_prepared_t *statement,
+ int nargs, const void **values)
+{
+ MYSQL_BIND *bind;
+ int ret;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
}
+
+ bind = apr_palloc(pool, nargs * sizeof(MYSQL_BIND));
+
+ dbd_mysql_bbind(pool, statement, nargs, values, bind);
+
+ ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
+
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
if (TXN_NOTICE_ERRORS(sql->trans)) {
#else
@@ -591,6 +989,77 @@
}
return ret;
}
+
+static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
+ apr_dbd_prepared_t *statement, va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+ for (i = 0; i < statement->nargs; i++) {
+ values[i] = va_arg(args, void*);
+ }
+
+ return dbd_mysql_pbquery(pool, sql, nrows, statement,
+ statement->nargs, values);
+}
+
+static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement, int random,
+ int nargs, const void **args)
+{
+ int ret;
+ MYSQL_BIND *bind;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ bind = apr_palloc(pool, nargs * sizeof(MYSQL_BIND));
+
+ dbd_mysql_bbind(pool, statement, nargs, args, bind);
+
+ ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
+
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+#else
+ if (sql->trans) {
+#endif
+ sql->trans->errnum = ret;
+ }
+ return ret;
+}
+
+static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
+ apr_dbd_results_t **res,
+ apr_dbd_prepared_t *statement, int random,
+ va_list args)
+{
+ const void **values;
+ int i;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ values = apr_palloc(pool, sizeof(*values) * statement->nargs);
+
+ for (i = 0; i < statement->nargs; i++) {
+ values[i] = va_arg(args, void*);
+ }
+
+ return dbd_mysql_pbselect(pool, sql, res, statement, random,
+ statement->nargs, values);
+}
+
static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
{
int ret = -1;
@@ -616,6 +1085,7 @@
* underlying DB supports them within MySQL. Unfortunately
* it fails silently with the default InnoDB.
*/
+
static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
apr_dbd_transaction_t **trans)
{
@@ -631,6 +1101,7 @@
handle->trans = *trans;
return (*trans)->errnum;
}
+
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans)
{
@@ -649,6 +1120,7 @@
return trans->mode = (mode & TXN_MODE_BITS);
}
#endif
+
static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params)
{
static const char *const delims = " \r\n\t;|,";
@@ -701,7 +1173,7 @@
++key;
for (value = ptr+1; isspace(*value); ++value);
vlen = strcspn(value, delims);
- for (i=0; fields[i].field != NULL; ++i) {
+ for (i = 0; fields[i].field != NULL; i++) {
if (!strncasecmp(fields[i].field, key, klen)) {
fields[i].value = apr_pstrndup(pool, value, vlen);
break;
@@ -736,25 +1208,30 @@
return sql;
}
+
static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
{
mysql_close(handle->conn);
return APR_SUCCESS;
}
+
static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool,
apr_dbd_t *handle)
{
return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS;
}
+
static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle,
const char* name)
{
return mysql_select_db(handle->conn, name);
}
+
static void *dbd_mysql_native(apr_dbd_t *handle)
{
return handle->conn;
}
+
static int dbd_mysql_num_cols(apr_dbd_results_t *res)
{
if (res->statement) {
@@ -764,6 +1241,7 @@
return mysql_num_fields(res->res);
}
}
+
static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
{
if (res->random) {
@@ -778,11 +1256,13 @@
return -1;
}
}
+
static apr_status_t thread_end(void *data)
{
mysql_thread_end();
return APR_SUCCESS;
}
+
static void dbd_mysql_init(apr_pool_t *pool)
{
my_init();
@@ -816,9 +1296,14 @@
,
dbd_mysql_get_name,
dbd_mysql_transaction_mode_get,
- dbd_mysql_transaction_mode_set
+ dbd_mysql_transaction_mode_set,
+ "?",
+ dbd_mysql_pvbquery,
+ dbd_mysql_pvbselect,
+ dbd_mysql_pbquery,
+ dbd_mysql_pbselect,
+ dbd_mysql_datum_get,
#endif
};
#endif
-