/src/libs/storage/db_mysql.c
C | 461 lines | 326 code | 82 blank | 53 comment | 78 complexity | 89c1e5e6790bb141f396e343d4e3df80 MD5 | raw file
Possible License(s): GPL-3.0
- /*
- * db_mysql.c
- *
- * Copyright (C) 2011 - Dr.NP
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /**
- * @version db_mysql.c 1.0
- * @package storage
- * @author Dr.NP <np@bsgroup.org>
- * @update 08/17/2011
- */
- /**
- * MySQL database operator
- *
- * === CHANGELOG ===
- * [08/17/2011] - Creation
- */
- #include "bsp.h"
- #include "bsp_conf.h"
- #include "bsp_hash.h"
- #include "bsp_object.h"
- #include "bsp_utils.h"
- #include "bsp_db_mysql.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <err.h>
- #include <sys/types.h>
- #include "mysql.h"
- #include "errmsg.h"
- struct mysql_conn *mysql_conn_list;
- size_t mysql_conn_list_size;
- int db_mysql_init()
- {
- mysql_conn_list = ensure_list_space(NULL, sizeof(struct mysql_conn), &mysql_conn_list_size, MYSQL_CONN_LIST_INITIAL);
- if (!mysql_conn_list)
- {
- err(RTN_ERROR_MEMORY_ALLOC, "MySQL connection list initial error!!!");
- }
- memset(mysql_conn_list, 0, sizeof(struct mysql_conn) * mysql_conn_list_size);
- debug_info("Storage :: MySQL initialized\n");
- return RTN_SUCCESS;
- }
- int db_mysql_connect(
- const char *host,
- const char *user,
- const char *pass,
- const char *db,
- int max_active_results
- )
- {
- // Caculate the hash value
- char srv_sig[4096];
- snprintf(srv_sig,
- 4095,
- "%s:%s@%s/%s#%d",
- user ? user : "",
- pass ? pass : "",
- host ? host : "",
- db ? db : "",
- max_active_results);
- u_int32_t srv_hash = hash(srv_sig, strlen(srv_sig));
- // Find available connection
- struct mysql_conn *curr = NULL;
- int i;
- for (i = 0; i < mysql_conn_list_size; i ++)
- {
- if ((!mysql_conn_list[i].mask & CONN_MASK_IN_USE) && mysql_conn_list[i].srv_hash == srv_hash)
- {
- // Available connection
- curr = &mysql_conn_list[i];
- curr->mask |= CONN_MASK_IN_USE;
- return curr->id;
- }
- }
- // Make a new one?
- for (i = 0; i < mysql_conn_list_size; i ++)
- {
- if (!mysql_conn_list[i].srv_hash && !mysql_conn_list[i].mask)
- {
- // An empty slot
- curr = &mysql_conn_list[i];
- curr->id = i;
- break;
- }
-
- }
- if (!curr)
- {
- // Enlarge conn list
- mysql_conn_list = ensure_list_space(mysql_conn_list, sizeof(struct mysql_conn), &mysql_conn_list_size, mysql_conn_list_size * 2);
- if (!mysql_conn_list)
- {
- err(RTN_ERROR_MEMORY_ALLOC, "MySQL connection list initial error!!!");
- }
- i = (mysql_conn_list_size / 2);
- memset(mysql_conn_list + i * sizeof(struct mysql_conn), 0, sizeof(struct mysql_conn));
- curr = &mysql_conn_list[i];
- curr->id = i;
- }
-
- const char *conn_host = (const char *) NULL;
- const char *conn_sock = (const char *) NULL;
- const char *conn_user = user ? user : "";
- // For the password, a value of the empty string in the mysql_real_connect()
- // call cannot be overridden in an option file, because the empty string indicates
- // explicitly that the MySQL account must have an empty password.
- const char *conn_pass = pass ? pass : (const char *) NULL;
- const char *conn_db = db ? db : (const char *) NULL;
-
- // If "host" is a local sock pipe?
- FILE *tfp = fopen(host, "r");
- if (tfp)
- {
- // Local sock file
- conn_sock = host;
- fclose(tfp);
- }
- else
- {
- conn_host = host;
- }
- curr->conn = mysql_init(NULL);
- if (!mysql_real_connect(curr->conn,
- conn_host,
- conn_user,
- conn_pass,
- conn_db,
- 0,
- conn_sock,
- 0))
- {
- return RTN_ERROR_DB_CONNECTION;
- }
- my_bool my_true = 0x1;
- mysql_options(curr->conn, MYSQL_OPT_RECONNECT, &my_true);
- // Set default charset
- mysql_query(curr->conn, "SET NAMES 'utf8'");
- // Set default database
- if (conn_db)
- {
- mysql_select_db(curr->conn, conn_db);
- }
- // Set hash
- curr->srv_hash = srv_hash;
- // Set mask
- curr->mask |= CONN_MASK_IN_USE;
- if (db && strlen(db))
- {
- curr->mask |= CONN_MASK_DB_SET;
- }
- curr->result_list_size = max_active_results;
- if (curr->result_list)
- {
- db_mysql_free_results(curr->id);
-
- free(curr->result_list);
- }
- curr->result_list = ensure_list_space(NULL, sizeof(MYSQL_RES *), NULL, max_active_results);
- memset(curr->result_list, 0, sizeof(MYSQL_RES *) * max_active_results);
- return curr->id;
- }
- int db_mysql_close(int conn_id)
- {
- struct mysql_conn *curr = NULL;
- if (conn_id >= 0 && conn_id < mysql_conn_list_size)
- {
- curr = &mysql_conn_list[conn_id];
- }
- if (curr && curr->conn)
- {
- db_mysql_free_results(curr->id);
- mysql_close(curr->conn);
- curr->mask &= ~CONN_MASK_IN_USE;
- }
- return RTN_SUCCESS;
- }
- int db_mysql_query(int conn_id, const char *query, size_t stmt_len)
- {
- struct mysql_conn *curr = NULL;
- int res, i;
- if (conn_id >= 0 && conn_id < mysql_conn_list_size)
- {
- curr = &mysql_conn_list[conn_id];
- }
-
- if (curr && curr->conn && curr->mask & CONN_MASK_IN_USE)
- {
- if ((signed) stmt_len < 0)
- {
- stmt_len = strlen(query);
- }
- res = mysql_real_query(curr->conn, query, stmt_len);
- if (0 == res)
- {
- // Query succssfully
- curr->queries ++;
- MYSQL_RES *qres = mysql_store_result(curr->conn);
- int res_id = -1;
- if (qres)
- {
- // SELECT / SHOW / DESCRIBE / EXPLAIN / CHECK TABLE ...
- // A result set returned, and stored into object
- // Find a empty result slot
- for (i = 0; i < curr->result_list_size; i ++)
- {
- if (!curr->result_list[i])
- {
- curr->result_list[i] = qres;
- res_id = i;
- return res_id;
- }
- }
- if (res_id < 0)
- {
- // Result list full, you have to free some used result first!!!
- return RTN_ERROR_DB_RESULT_FULL;
- }
- }
- else
- {
- // Statement didn't return a result set
- }
-
- return RTN_SUCCESS;
- }
- else if (CR_SERVER_GONE_ERROR == res || CR_SERVER_LOST == res)
- {
- // MySQL gone
- db_mysql_close(curr->id);
- return RTN_ERROR_DB_CONNECTION;
- }
- else
- {
- return RTN_ERROR_DB_UNKNOWN;
- }
- }
- else
- {
- res = RTN_ERROR_DB_UNAVAILABLE;
- }
- return res;
- }
- const char * db_mysql_error(int conn_id)
- {
- struct mysql_conn *curr = NULL;
-
- if (conn_id >= 0 && conn_id < mysql_conn_list_size)
- {
- curr = &mysql_conn_list[conn_id];
- }
- if (curr && curr->conn && curr->mask & CONN_MASK_IN_USE)
- {
- return mysql_error(curr->conn);
- }
- return "";
- }
- int db_mysql_free_result(int conn_id, int res_id)
- {
- struct mysql_conn *curr = NULL;
-
- if (conn_id >= 0 && conn_id < mysql_conn_list_size)
- {
- curr = &mysql_conn_list[conn_id];
- }
- else
- {
- return RTN_ERROR_DB_UNAVAILABLE;
- }
- if (curr && curr->conn && curr->result_list && res_id >= 0 && res_id < curr->result_list_size)
- {
- if (curr->result_list[res_id])
- {
- mysql_free_result(curr->result_list[res_id]);
- curr->result_list[res_id] = NULL;
- }
- }
- else
- {
- return RTN_ERROR_DB_INVALID_RESULT;
- }
- return RTN_SUCCESS;
- }
- int db_mysql_free_results(int conn_id)
- {
- struct mysql_conn *curr = NULL;
-
- if (conn_id >= 0 && conn_id < mysql_conn_list_size)
- {
- curr = &mysql_conn_list[conn_id];
- }
- else
- {
- return RTN_ERROR_DB_UNAVAILABLE;
- }
- if (curr && curr->conn && curr->result_list)
- {
- int i;
- for (i = 0; i < curr->result_list_size; i ++)
- {
- if (curr->result_list[i])
- {
- mysql_free_result(curr->result_list[i]);
- curr->result_list[i] = NULL;
- }
- }
- }
- return RTN_SUCCESS;
- }
- u_int64_t db_mysql_num_rows(int conn_id, int res_id)
- {
- struct mysql_conn *curr = NULL;
- u_int64_t rows = 0;
-
- if (conn_id >= 0 && conn_id < mysql_conn_list_size)
- {
- curr = &mysql_conn_list[conn_id];
- }
- else
- {
- return RTN_ERROR_DB_UNAVAILABLE;
- }
- if (curr && curr->conn && curr->result_list && res_id >= 0 && res_id < curr->result_list_size)
- {
- if (curr->result_list[res_id])
- {
- rows = mysql_num_rows(curr->result_list[res_id]);
- }
- }
- return rows;
- }
- int db_mysql_fetch_row(int conn_id, int res_id, struct object_t *obj)
- {
- struct mysql_conn *curr = NULL;
- struct kv_t *item;
- MYSQL_ROW row;
- MYSQL_FIELD *fields;
- unsigned long *lengths;
- u_int32_t num_fields;
- int i;
- if (!obj)
- {
- return RTN_ERROR_MEMORY_OBJECT;
- }
- if (conn_id >= 0 && conn_id < mysql_conn_list_size)
- {
- curr = &mysql_conn_list[conn_id];
- }
- else
- {
- return RTN_ERROR_DB_UNAVAILABLE;
- }
- if (curr && curr->conn && curr->result_list && res_id >= 0 && res_id < curr->result_list_size)
- {
- if (curr->result_list[res_id])
- {
- row = mysql_fetch_row(curr->result_list[res_id]);
- fields = mysql_fetch_fields(curr->result_list[res_id]);
- lengths = mysql_fetch_lengths(curr->result_list[res_id]);
- num_fields = mysql_num_fields(curr->result_list[res_id]);
- if (row && fields && lengths)
- {
- for (i = 0; i < num_fields; i ++)
- {
- // Store result
- item = alloc_item((const char *) fields[i].name, -1);
- set_string_item(item, (const char *) row[i], lengths[i]);
- insert_item(obj, item);
- }
- return num_fields;
- }
- }
- }
- return RTN_SUCCESS;
- }