apr_dbd_mysql.c
来自「linux网络服务器工具」· C语言 代码 · 共 1,277 行 · 第 1/3 页
C
1,277 行
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#include "apu.h"#define HAVE_MYSQL_MYSQL_H#if APU_HAVE_MYSQL#include "apu_version.h"#include "apu_config.h"#include <ctype.h>#include <stdlib.h>#ifdef HAVE_MYSQL_H#include <mysql.h>#include <errmsg.h>#elif defined(HAVE_MYSQL_MYSQL_H)#include <mysql/mysql.h>#include <mysql/errmsg.h>#endif#include "apr_strings.h"#include "apr_buckets.h"#include "apr_dbd_internal.h"/* default maximum field size 1 MB */#define FIELDSIZE 1048575struct apr_dbd_prepared_t { MYSQL_STMT* stmt; int nargs; int nvals; apr_dbd_type_e *types;};struct apr_dbd_transaction_t { int mode; int errnum; apr_dbd_t *handle;};struct apr_dbd_t { MYSQL* conn ; apr_dbd_transaction_t* trans ; unsigned long fldsz;};struct apr_dbd_results_t { int random; MYSQL_RES *res; MYSQL_STMT *statement; MYSQL_BIND *bind; apr_pool_t *pool;};struct apr_dbd_row_t { MYSQL_ROW row; apr_dbd_results_t *res; unsigned long *len;};/* MySQL specific bucket for BLOB types */typedef struct apr_bucket_lob apr_bucket_lob;/** * A bucket referring to a MySQL BLOB */struct apr_bucket_lob { /** 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 lob_bucket_destroy(void *data);static apr_status_t lob_bucket_read(apr_bucket *e, const char **str, apr_size_t *len, apr_read_type_e block);static apr_bucket *apr_bucket_lob_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_lob_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);static const apr_bucket_type_t apr_bucket_type_lob = { "LOB", 5, APR_BUCKET_DATA, lob_bucket_destroy, lob_bucket_read, apr_bucket_setaside_notimpl, apr_bucket_shared_split, apr_bucket_shared_copy};static void lob_bucket_destroy(void *data){ apr_bucket_lob *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 lob_bucket_read(apr_bucket *e, const char **str, apr_size_t *len, apr_read_type_e block){ apr_bucket_lob *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 LOB bucket */ b = apr_bucket_alloc(sizeof(*b), e->list); b->start = boffset + *len; b->length = blength; b->data = a; b->type = &apr_bucket_type_lob; b->free = apr_bucket_free; b->list = e->list; APR_BUCKET_INSERT_AFTER(e, b); } else { lob_bucket_destroy(a); } return APR_SUCCESS;}static apr_bucket *apr_bucket_lob_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_lob *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_lob; return b;}static apr_bucket *apr_bucket_lob_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_lob_make(b, row, col, offset, len, p);}static apr_status_t free_result(void *data){ mysql_free_result(data); return APR_SUCCESS;}static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql, apr_dbd_results_t **results, const char *query, int seek){ int sz; int ret; if (sql->trans && sql->trans->errnum) { return sql->trans->errnum; } ret = mysql_query(sql->conn, query); if (!ret) { if (sz = mysql_field_count(sql->conn), sz > 0) { if (!*results) { *results = apr_palloc(pool, sizeof(apr_dbd_results_t)); } (*results)->random = seek; (*results)->statement = NULL; (*results)->pool = pool; if (seek) { (*results)->res = mysql_store_result(sql->conn); } else { (*results)->res = mysql_use_result(sql->conn); } apr_pool_cleanup_register(pool, (*results)->res, free_result,apr_pool_cleanup_null); } } else { ret = mysql_errno(sql->conn); } if (TXN_NOTICE_ERRORS(sql->trans)) { sql->trans->errnum = ret; } return ret;}static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n){ if ((n < 0) || (n >= mysql_num_fields(res->res))) { return NULL; } return mysql_fetch_fields(res->res)[n].name;}static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res, apr_dbd_row_t **row, int rownum){ MYSQL_ROW r = NULL; int ret = 0; if (res->statement) { if (res->random) { if (rownum > 0) { mysql_stmt_data_seek(res->statement, (my_ulonglong) --rownum); } else { return -1; /* invalid row */ } } ret = mysql_stmt_fetch(res->statement); switch (ret) { case 1: ret = mysql_stmt_errno(res->statement); break; case MYSQL_NO_DATA: ret = -1; break; default: ret = 0; /* bad luck - get_entry will deal with this */ break; } } else { if (res->random) { if (rownum > 0) { mysql_data_seek(res->res, (my_ulonglong) --rownum); } else { return -1; /* invalid row */ } } r = mysql_fetch_row(res->res); if (r == NULL) { ret = -1; } } if (ret == 0) { if (!*row) { *row = apr_palloc(pool, sizeof(apr_dbd_row_t)); } (*row)->row = r; (*row)->res = res; (*row)->len = mysql_fetch_lengths(res->res); } else { apr_pool_cleanup_run(pool, res->res, free_result); } return ret;}#if 0/* An improved API that was proposed but not followed up */static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n, apr_dbd_datum_t *val){ MYSQL_BIND *bind; if (row->res->statement) { bind = &row->res->bind[n]; if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { val->type = APR_DBD_VALUE_NULL; return -1; } if (*bind->is_null) { val->type = APR_DBD_VALUE_NULL; return -1; } else { val->type = APR_DBD_VALUE_STRING; val->value.stringval = bind->buffer; } } else { val->type = APR_DBD_VALUE_STRING; val->value.stringval = row->row[n]; } return 0;}#elsestatic const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n){ MYSQL_BIND *bind; if (row->res->statement) { bind = &row->res->bind[n]; if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) { return NULL; } if (*bind->is_null) { return NULL; } else { return bind->buffer; } } else { return row->row[n]; } return NULL;}#endifstatic 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:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?