Quoting Bojan Smojver <[EMAIL PROTECTED]>:
And here is the patch that does exactly that. I'll follow this up with another e-mail where I'll include some other, related stuff. This is due to list size restrictions.
As promised... -- 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,10 @@
struct apr_dbd_prepared_t {
MYSQL_STMT* stmt;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ int nargs;
+ apr_dbd_type_e *types;
+#endif
};
struct apr_dbd_transaction_t {
@@ -83,12 +88,156 @@
MYSQL_RES *res;
MYSQL_STMT *statement;
MYSQL_BIND *bind;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ apr_pool_t *pool;
+#endif
};
struct apr_dbd_row_t {
MYSQL_ROW row;
apr_dbd_results_t *res;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ unsigned long *len;
+#endif
};
+#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;
+ 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 */
+
+ /* fetch from offset if not at the beginning */
+ if (boffset > 0) {
+ rv = mysql_stmt_fetch_column(res->statement, bind, col, boffset);
+ if (rv != 0) {
+ return APR_EGENERAL;
+ }
+ }
+ blength -= blength > bind->buffer_length ? bind->buffer_length : blength;
+ *len = e->length - blength;
+ *str = bind->buffer;
+
+ /* allocate new buffer, since we used this one for the bucket */
+ bind->buffer = apr_palloc(res->pool, bind->buffer_length);
+
+ /*
+ * Change the current bucket to refer to what we read,
+ * even if we read nothing because we hit EOF.
+ */
+ apr_bucket_pool_make(e, *str, *len, res->pool);
+
+ /* If we have more to read from the field, then create another bucket */
+ if (blength > 0) {
+ /* 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 + *len;
+ 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);
@@ -112,6 +261,9 @@
}
(*results)->random = seek;
(*results)->statement = NULL;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ (*results)->pool = pool;
+#endif
if (seek) {
(*results)->res = mysql_store_result(sql->conn);
}
@@ -145,6 +297,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)
{
@@ -187,6 +340,9 @@
}
(*row)->row = r;
(*row)->res = res;
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ (*row)->len = mysql_fetch_lengths(res->res);
+#endif
}
else {
apr_pool_cleanup_run(pool, res->res, free_result);
@@ -221,6 +377,7 @@
return 0;
}
#else
+
static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
{
MYSQL_BIND *bind;
@@ -239,13 +396,166 @@
else {
return row->row[n];
}
+ return NULL;
+}
+#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 = *bind->length;
+
+ 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,
+ row->res->pool, 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_pool_create(row->row[n], row->len[n],
+ row->res->pool, 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 +576,7 @@
}
return ret;
}
+
static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
apr_dbd_t *sql)
{
@@ -274,16 +585,486 @@
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;
}
+
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
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 */
+ int ret;
+
+ if (!*statement) {
+ *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
+ }
+ (*statement)->stmt = mysql_stmt_init(sql->conn);
+
+ if ((*statement)->stmt) {
+ apr_pool_cleanup_register(pool, (*statement)->stmt,
+ stmt_close, apr_pool_cleanup_null);
+ 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 void dbd_mysql_bind(apr_dbd_prepared_t *statement,
+ int nargs, const char **values, MYSQL_BIND *bind)
+{
+ int i, j;
+ my_bool is_null = FALSE;
+
+ 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_LONG_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;
+ 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);
+ }
+
+ return ret;
+}
+
+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;
+ int ret;
+
+ if (sql->trans && sql->trans->errnum) {
+ return sql->trans->errnum;
+ }
+
+ bind = apr_palloc(pool, nargs * sizeof(MYSQL_BIND));
+
+ dbd_mysql_bind(statement, nargs, values, bind);
+
+ ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
+
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ 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)
+{
+ const char **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, 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);
+ if (!ret) {
+ if (!*res) {
+ *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
+ }
+ (*res)->random = random;
+ (*res)->statement = statement->stmt;
+ (*res)->res = mysql_stmt_result_metadata(statement->stmt);
+ (*res)->pool = pool;
+ 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;
+ if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) {
+ (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
+ }
+ else {
+ (*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
+ }
+ }
+ ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
+ if (!ret) {
+ ret = mysql_stmt_store_result(statement->stmt);
+ }
+ }
+ }
+ 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 (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ 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;
+
+ 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, 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;
+
+ 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;
+ }
+ else {
+ bind[i].buffer = apr_palloc(pool, sizeof(int));
+ *(int*)bind[i].buffer = *(long*)arg;
+ }
+ 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_LONG_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;
+ }
+ }
+
+ 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 (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
+ 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 (TXN_NOTICE_ERRORS(sql->trans)) {
+ 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);
+}
+#else
+static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
+ const char *query, const char *label,
+ 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;
@@ -326,6 +1107,7 @@
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)
@@ -364,15 +1146,12 @@
}
*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)
{
@@ -411,15 +1190,12 @@
}
*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_pselect(apr_pool_t *pool, apr_dbd_t *sql,
apr_dbd_results_t **res,
apr_dbd_prepared_t *statement, int random,
@@ -496,15 +1272,12 @@
if (ret != 0) {
ret = mysql_stmt_errno(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_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
apr_dbd_results_t **res,
apr_dbd_prepared_t *statement, int random,
@@ -582,15 +1355,13 @@
if (ret != 0) {
ret = mysql_stmt_errno(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;
}
+#endif
+
static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
{
int ret = -1;
@@ -616,6 +1387,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 +1403,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 +1422,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 +1475,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 +1510,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 +1543,7 @@
return mysql_num_fields(res->res);
}
}
+
static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
{
if (res->random) {
@@ -778,11 +1558,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 +1598,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
-
build.sh
Description: application/shellscript
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <apr.h>
#include <apr_pools.h>
#include <apr_strings.h>
#include <apr_time.h>
#include <apr_dbd.h>
#include <apr_buckets.h>
#define NARGS 21
#define BLOB_SIZE 100000
int main(int argc,char **argv){
apr_pool_t *pool;
const char *drivers[][2] = {{"pgsql","dbname=test"},
{"mysql","dbname=test,fldsz=8000"},
{"sqlite2","./test2.db"},
{"sqlite3","./test3.db"},
{NULL,NULL}};
size_t i,j;
int nrows, has_prepare;
const apr_dbd_driver_t *driver;
apr_dbd_t *sql;
apr_dbd_prepared_t *stmt;
apr_dbd_results_t *res;
apr_dbd_row_t *row;
apr_status_t rv,rv2[NARGS];
char d_tiny,d_tiny2;
unsigned char d_utiny,d_utiny2;
short d_short,d_short2;
unsigned short d_ushort,d_ushort2;
int d_int,d_int2;
unsigned int d_uint,d_uint2;
long d_long,d_long2;
unsigned long d_ulong,d_ulong2;
apr_int64_t d_longlong,d_longlong2;
apr_uint64_t d_ulonglong,d_ulonglong2;
float d_float,d_float2;
double d_double,d_double2;
char *d_string,*d_text,*d_time,*d_date,*d_datetime,*d_timestamp,*d_ztimestamp,
*d_string2,*d_text2,*d_time2,*d_date2,*d_datetime2,*d_timestamp2,
*d_ztimestamp2,*d_null2,*buf,*query;
apr_dbd_blob_t *d_blob;
apr_bucket_alloc_t *balloc;
apr_bucket_brigade *d_blob2;
apr_off_t blen;
apr_size_t blen2;
const char *args[NARGS];
const void *vargs[NARGS];
char *data;
int correct;
apr_initialize();
apr_pool_create(&pool,NULL);
apr_dbd_init(pool);
balloc=apr_bucket_alloc_create(pool);
for(i=0;drivers[i][0];i++){
driver=NULL;
rv=apr_dbd_get_driver(pool,drivers[i][0],&driver);
if(rv == APR_SUCCESS){
sql=NULL;
rv=apr_dbd_open(driver,pool,drivers[i][1],&sql);
if(rv == APR_SUCCESS){
stmt=NULL;
res=NULL;
row=NULL;
printf("driver[%u]: name=%s, conn=%s, loaded=%p\n",i,drivers[i][0],
drivers[i][1],sql);
rv=apr_dbd_query(driver,sql,&nrows,"delete from test");
if(rv != APR_SUCCESS)
continue;
printf("table cleared, rows affected=%d\n",nrows);
rv=apr_dbd_prepare(driver,pool,sql,
"insert into test values("
"%hhd, %hhu, %hd, %hu, %d, %u, %ld, %lu, "
"%lld, %llu, %f, %lf, %s, "
"%pDt, %pDi, %pDd, %pDa, %pDs, %pDz, %pDb, %pDn)",
"apu_dbd_insert",&stmt);
switch(rv){
case APR_ENOTIMPL:
has_prepare=0;
break;
case APR_SUCCESS:
has_prepare=1;
break;
default:
continue;
}
printf("prepared statement=%p\n",stmt);
d_tiny=-1;
d_utiny=1;
d_short=-2;
d_ushort=2;
d_int=-3;
d_uint=3;
d_long=-4;
d_ulong=4;
d_longlong=-5;
d_ulonglong=5;
d_float=6;
d_double=7;
d_string="string";
d_text="text";
d_time="11:22";
d_date="2006-08-29";
d_datetime="2006-08-29 11:22";
d_timestamp="2006-08-29 11:22";
d_ztimestamp="2006-08-29 11:22";
data=apr_palloc(pool,BLOB_SIZE+1);
memset((void*)data,'b',BLOB_SIZE);
((char*)(data))[BLOB_SIZE]='\0';
d_blob=apr_dbd_blob_create(pool,data,BLOB_SIZE,NULL,NULL);
args[0]=apr_psprintf(pool,"%hd",d_tiny);
args[1]=apr_psprintf(pool,"%hu",d_utiny);
args[2]=apr_psprintf(pool,"%hd",d_short);
args[3]=apr_psprintf(pool,"%hu",d_ushort);
args[4]=apr_psprintf(pool,"%d",d_int);
args[5]=apr_psprintf(pool,"%u",d_uint);
args[6]=apr_psprintf(pool,"%ld",d_long);
args[7]=apr_psprintf(pool,"%lu",d_ulong);
args[8]=apr_psprintf(pool,"%" APR_INT64_T_FMT,d_longlong);
args[9]=apr_psprintf(pool,"%" APR_UINT64_T_FMT,d_ulonglong);
args[10]=apr_psprintf(pool,"%f",d_float);
args[11]=apr_psprintf(pool,"%lf",d_double);
args[12]=d_string;
args[13]=d_text;
args[14]=d_time;
args[15]=d_date;
args[16]=d_datetime;
args[17]=d_timestamp;
args[18]=d_ztimestamp;
args[19]=apr_psprintf(pool,"%" APR_SIZE_T_FMT ":::%s",
apr_dbd_blob_size_get(d_blob),
apr_dbd_blob_data_get(d_blob));
args[20]=NULL;
for(j=0;j<NARGS;j++)
printf("args[%u]=%.60s\n",j,args[j]);
if(has_prepare){
rv=apr_dbd_pquery(driver,pool,sql,&nrows,stmt,NARGS,args);
if(rv != APR_SUCCESS)
continue;
} else{
query="";
query=apr_pstrcat(pool,"insert into test values('",
args[0],"','",
args[1],"','",
args[2],"','",
args[3],"','",
args[4],"','",
args[5],"','",
args[6],"','",
args[7],"','",
args[8],"','",
args[9],"','",
args[10],"','",
args[11],"','",
args[12],"','",
args[13],"','",
args[14],"','",
args[16],"','",
args[16],"','",
args[17],"','",
args[18],"','",
apr_dbd_blob_data_get(d_blob),"',NULL)",NULL);
rv=apr_dbd_query(driver,sql,&nrows,query);
if(rv != APR_SUCCESS)
continue;
}
printf("query status=%d\n",rv);
vargs[0]=&d_tiny;
vargs[1]=&d_utiny;
vargs[2]=&d_short;
vargs[3]=&d_ushort;
vargs[4]=&d_int;
vargs[5]=&d_uint;
vargs[6]=&d_long;
vargs[7]=&d_ulong;
vargs[8]=&d_longlong;
vargs[9]=&d_ulonglong;
vargs[10]=&d_float;
vargs[11]=&d_double;
vargs[12]=d_string;
vargs[13]=d_text;
vargs[14]=d_time;
vargs[15]=d_date;
vargs[16]=d_datetime;
vargs[17]=d_timestamp;
vargs[18]=d_ztimestamp;
vargs[19]=d_blob;
vargs[20]=NULL;
if(has_prepare){
rv=apr_dbd_pbquery(driver,pool,sql,&nrows,stmt,NARGS,vargs);
if(rv != APR_SUCCESS)
continue;
} else{
rv=apr_dbd_query(driver,sql,&nrows,query);
if(rv != APR_SUCCESS)
continue;
}
printf("query status=%d\n",rv);
if(has_prepare){
rv=apr_dbd_prepare(driver,pool,sql,"select * from test",
"apu_dbd_select",&stmt);
if(rv != APR_SUCCESS)
continue;
printf("prepared statement=%p\n",stmt);
res=NULL;
rv = apr_dbd_pbselect(driver,pool,sql,&res,stmt,0,0,NULL);
if(rv != APR_SUCCESS)
continue;
} else{
rv=apr_dbd_select(driver,pool,sql,&res,"select * from test",0);
if(rv != APR_SUCCESS)
continue;
}
printf("result=%p\n",res);
while(!apr_dbd_get_row(driver,pool,res,&row,-1)){
d_blob2=apr_brigade_create(pool,balloc);
rv2[0]=apr_dbd_datum_get(driver,row,0,APR_DBD_TYPE_TINY,&d_tiny2);
rv2[1]=apr_dbd_datum_get(driver,row,1,APR_DBD_TYPE_UTINY,&d_utiny2);
rv2[2]=apr_dbd_datum_get(driver,row,2,APR_DBD_TYPE_SHORT,&d_short2);
rv2[3]=apr_dbd_datum_get(driver,row,3,APR_DBD_TYPE_USHORT,&d_ushort2);
rv2[4]=apr_dbd_datum_get(driver,row,4,APR_DBD_TYPE_INT,&d_int2);
rv2[5]=apr_dbd_datum_get(driver,row,5,APR_DBD_TYPE_UINT,&d_uint2);
rv2[6]=apr_dbd_datum_get(driver,row,6,APR_DBD_TYPE_LONG,&d_long2);
rv2[7]=apr_dbd_datum_get(driver,row,7,APR_DBD_TYPE_ULONG,&d_ulong2);
rv2[8]=apr_dbd_datum_get(driver,row,8,APR_DBD_TYPE_LONGLONG,&d_longlong2);
rv2[9]=apr_dbd_datum_get(driver,row,9,APR_DBD_TYPE_ULONGLONG,&d_ulonglong2);
rv2[10]=apr_dbd_datum_get(driver,row,10,APR_DBD_TYPE_FLOAT,&d_float2);
rv2[11]=apr_dbd_datum_get(driver,row,11,APR_DBD_TYPE_DOUBLE,&d_double2);
rv2[12]=apr_dbd_datum_get(driver,row,12,APR_DBD_TYPE_STRING,&d_string2);
rv2[13]=apr_dbd_datum_get(driver,row,13,APR_DBD_TYPE_TEXT,&d_text2);
rv2[14]=apr_dbd_datum_get(driver,row,14,APR_DBD_TYPE_TIME,&d_time2);
rv2[15]=apr_dbd_datum_get(driver,row,15,APR_DBD_TYPE_DATE,&d_date2);
rv2[16]=apr_dbd_datum_get(driver,row,16,APR_DBD_TYPE_DATETIME,&d_datetime2);
rv2[17]=apr_dbd_datum_get(driver,row,17,APR_DBD_TYPE_TIMESTAMP,&d_timestamp2);
rv2[18]=apr_dbd_datum_get(driver,row,18,APR_DBD_TYPE_ZTIMESTAMP,&d_ztimestamp2);
rv2[19]=apr_dbd_datum_get(driver,row,19,APR_DBD_TYPE_BLOB,d_blob2);
rv2[20]=apr_dbd_datum_get(driver,row,20,APR_DBD_TYPE_NULL,&d_null2);
for(j=0;j<NARGS;j++){
switch(rv2[j]){
case APR_ENOENT:
printf("value[%d] is NULL\n",j);
break;
case APR_EGENERAL:
printf("error fetching value[%d]\n",j);
break;
}
}
printf("%hhd %hhu %hd %hu %d %u %ld %lu "
"%" APR_INT64_T_FMT " %" APR_UINT64_T_FMT " %f %lf "
"%s %s %s %s %s %s %s %p %p\n",
d_tiny2,d_utiny2,d_short2,d_ushort2,d_int2,d_uint2,
d_long2,d_ulong2,d_longlong2,d_ulonglong2,d_float2,d_double2,
d_string2,d_text2,d_time2,d_date2,d_datetime2,d_timestamp2,
d_ztimestamp2,d_blob2,d_null2);
apr_brigade_length(d_blob2,1,&blen);
blen2=blen;
buf=NULL;
apr_brigade_pflatten(d_blob2,&buf,&blen2,pool);
correct=1;
for(j=0;j<blen;j++)
if(buf[j]!='b'){
correct=0;
break;
}
printf("blob=%.*s, length=%"
APR_OFF_T_FMT ", correct=%d\n",40,buf,blen,correct);
apr_brigade_destroy(d_blob2);
}
}
}
}
apr_terminate();
return 0;
}
