|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by MIT license that can be found in the
|
|
|
|
|
* LICENSE file in the root of the source tree. All contributing project authors
|
|
|
|
|
* may be found in the AUTHORS file in the root of the source tree.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef SQL_SQLCONNECTION_H_
|
|
|
|
|
#define SQL_SQLCONNECTION_H_
|
|
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdarg>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <list>
|
|
|
|
|
#include <deque>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include "logger.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include <mysql.h>
|
|
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
#pragma comment (lib,"libmysql")
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace toolkit {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 数据库异常类
|
|
|
|
|
* Database exception class
|
|
|
|
|
|
|
|
|
|
* [AUTO-TRANSLATED:f92df85e]
|
|
|
|
|
*/
|
|
|
|
|
class SqlException : public std::exception {
|
|
|
|
|
public:
|
|
|
|
|
SqlException(const std::string &sql, const std::string &err) {
|
|
|
|
|
_sql = sql;
|
|
|
|
|
_err = err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual const char *what() const noexcept {
|
|
|
|
|
return _err.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string &getSql() const {
|
|
|
|
|
return _sql;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::string _sql;
|
|
|
|
|
std::string _err;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* mysql连接
|
|
|
|
|
* MySQL connection
|
|
|
|
|
|
|
|
|
|
* [AUTO-TRANSLATED:a2deb48d]
|
|
|
|
|
*/
|
|
|
|
|
class SqlConnection {
|
|
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* 构造函数
|
|
|
|
|
* @param url 数据库地址
|
|
|
|
|
* @param port 数据库端口号
|
|
|
|
|
* @param dbname 数据库名
|
|
|
|
|
* @param username 用户名
|
|
|
|
|
* @param password 用户密码
|
|
|
|
|
* @param character 字符集
|
|
|
|
|
* Constructor
|
|
|
|
|
* @param url Database address
|
|
|
|
|
* @param port Database port number
|
|
|
|
|
* @param dbname Database name
|
|
|
|
|
* @param username Username
|
|
|
|
|
* @param password User password
|
|
|
|
|
* @param character Character set
|
|
|
|
|
|
|
|
|
|
* [AUTO-TRANSLATED:410a33a6]
|
|
|
|
|
*/
|
|
|
|
|
SqlConnection(const std::string &url, unsigned short port,
|
|
|
|
|
const std::string &dbname, const std::string &username,
|
|
|
|
|
const std::string &password, const std::string &character = "utf8mb4") {
|
|
|
|
|
mysql_init(&_sql);
|
|
|
|
|
unsigned int timeout = 3;
|
|
|
|
|
mysql_options(&_sql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
|
|
|
|
|
if (!mysql_real_connect(&_sql, url.data(), username.data(),
|
|
|
|
|
password.data(), dbname.data(), port, nullptr, 0)) {
|
|
|
|
|
mysql_close(&_sql);
|
|
|
|
|
throw SqlException("mysql_real_connect", mysql_error(&_sql));
|
|
|
|
|
}
|
|
|
|
|
//兼容bool与my_bool [AUTO-TRANSLATED:7d8d4190]
|
|
|
|
|
//Compatible with bool and my_bool
|
|
|
|
|
uint32_t reconnect = 0x01010101;
|
|
|
|
|
mysql_options(&_sql, MYSQL_OPT_RECONNECT, &reconnect);
|
|
|
|
|
mysql_set_character_set(&_sql, character.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~SqlConnection() {
|
|
|
|
|
mysql_close(&_sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 以printf样式执行sql,无数据返回
|
|
|
|
|
* @param rowId insert时的插入rowid
|
|
|
|
|
* @param fmt printf类型fmt
|
|
|
|
|
* @param arg 可变参数列表
|
|
|
|
|
* @return 影响行数
|
|
|
|
|
* Execute SQL in printf style, no data returned
|
|
|
|
|
* @param rowId Insert rowid when inserting
|
|
|
|
|
* @param fmt printf type fmt
|
|
|
|
|
* @param arg Variable argument list
|
|
|
|
|
* @return Affected rows
|
|
|
|
|
|
|
|
|
|
* [AUTO-TRANSLATED:7c72ab80]
|
|
|
|
|
*/
|
|
|
|
|
template<typename Fmt, typename ...Args>
|
|
|
|
|
int64_t query(int64_t &rowId, Fmt &&fmt, Args &&...arg) {
|
|
|
|
|
check();
|
|
|
|
|
auto tmp = queryString(std::forward<Fmt>(fmt), std::forward<Args>(arg)...);
|
|
|
|
|
if (doQuery(tmp)) {
|
|
|
|
|
throw SqlException(tmp, mysql_error(&_sql));
|
|
|
|
|
}
|
|
|
|
|
rowId = mysql_insert_id(&_sql);
|
|
|
|
|
return mysql_affected_rows(&_sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 以printf样式执行sql,并且返回list类型的结果(不包含数据列名)
|
|
|
|
|
* @param rowId insert时的插入rowid
|
|
|
|
|
* @param ret 返回数据列表
|
|
|
|
|
* @param fmt printf类型fmt
|
|
|
|
|
* @param arg 可变参数列表
|
|
|
|
|
* @return 影响行数
|
|
|
|
|
* Execute SQL in printf style, and return list type result (excluding column names)
|
|
|
|
|
* @param rowId Insert rowid when inserting
|
|
|
|
|
* @param ret Returned data list
|
|
|
|
|
* @param fmt printf type fmt
|
|
|
|
|
* @param arg Variable argument list
|
|
|
|
|
* @return Affected rows
|
|
|
|
|
|
|
|
|
|
* [AUTO-TRANSLATED:57baa44e]
|
|
|
|
|
*/
|
|
|
|
|
template<typename Fmt, typename ...Args>
|
|
|
|
|
int64_t query(int64_t &rowId, std::vector<std::vector<std::string> > &ret, Fmt &&fmt, Args &&...arg) {
|
|
|
|
|
return queryList(rowId, ret, std::forward<Fmt>(fmt), std::forward<Args>(arg)...);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename Fmt, typename... Args>
|
|
|
|
|
int64_t query(int64_t &rowId, std::vector<std::list<std::string>> &ret, Fmt &&fmt, Args &&...arg) {
|
|
|
|
|
return queryList(rowId, ret, std::forward<Fmt>(fmt), std::forward<Args>(arg)...);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename Fmt, typename ...Args>
|
|
|
|
|
int64_t query(int64_t &rowId, std::vector<std::deque<std::string> > &ret, Fmt &&fmt, Args &&...arg) {
|
|
|
|
|
return queryList(rowId, ret, std::forward<Fmt>(fmt), std::forward<Args>(arg)...);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 以printf样式执行sql,并且返回Map类型的结果(包含数据列名)
|
|
|
|
|
* @param rowId insert时的插入rowid
|
|
|
|
|
* @param ret 返回数据列表
|
|
|
|
|
* @param fmt printf类型fmt
|
|
|
|
|
* @param arg 可变参数列表
|
|
|
|
|
* @return 影响行数
|
|
|
|
|
* Execute SQL in printf style, and return Map type result (including column names)
|
|
|
|
|
* @param rowId Insert rowid when inserting
|
|
|
|
|
* @param ret Returned data list
|
|
|
|
|
* @param fmt printf type fmt
|
|
|
|
|
* @param arg Variable argument list
|
|
|
|
|
* @return Affected rows
|
|
|
|
|
|
|
|
|
|
* [AUTO-TRANSLATED:a12a695e]
|
|
|
|
|
*/
|
|
|
|
|
template<typename Map, typename Fmt, typename ...Args>
|
|
|
|
|
int64_t query(int64_t &rowId, std::vector<Map> &ret, Fmt &&fmt, Args &&...arg) {
|
|
|
|
|
check();
|
|
|
|
|
auto tmp = queryString(std::forward<Fmt>(fmt), std::forward<Args>(arg)...);
|
|
|
|
|
if (doQuery(tmp)) {
|
|
|
|
|
throw SqlException(tmp, mysql_error(&_sql));
|
|
|
|
|
}
|
|
|
|
|
ret.clear();
|
|
|
|
|
MYSQL_RES *res = mysql_store_result(&_sql);
|
|
|
|
|
if (!res) {
|
|
|
|
|
rowId = mysql_insert_id(&_sql);
|
|
|
|
|
return mysql_affected_rows(&_sql);
|
|
|
|
|
}
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned int column = mysql_num_fields(res);
|
|
|
|
|
MYSQL_FIELD *fields = mysql_fetch_fields(res);
|
|
|
|
|
while ((row = mysql_fetch_row(res)) != nullptr) {
|
|
|
|
|
ret.emplace_back();
|
|
|
|
|
auto &back = ret.back();
|
|
|
|
|
for (unsigned int i = 0; i < column; i++) {
|
|
|
|
|
back[std::string(fields[i].name, fields[i].name_length)] = (row[i] ? row[i] : "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mysql_free_result(res);
|
|
|
|
|
rowId = mysql_insert_id(&_sql);
|
|
|
|
|
return mysql_affected_rows(&_sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string escape(const std::string &str) {
|
|
|
|
|
char *out = new char[str.length() * 2 + 1];
|
|
|
|
|
mysql_real_escape_string(&_sql, out, str.c_str(), str.size());
|
|
|
|
|
std::string ret(out);
|
|
|
|
|
delete[] out;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename ...Args>
|
|
|
|
|
static std::string queryString(const char *fmt, Args &&...arg) {
|
|
|
|
|
char *ptr_out = nullptr;
|
|
|
|
|
if (asprintf(&ptr_out, fmt, arg...) > 0 && ptr_out) {
|
|
|
|
|
std::string ret(ptr_out);
|
|
|
|
|
free(ptr_out);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename ...Args>
|
|
|
|
|
static std::string queryString(const std::string &fmt, Args &&...args) {
|
|
|
|
|
return queryString(fmt.data(), std::forward<Args>(args)...);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *queryString(const char *fmt) {
|
|
|
|
|
return fmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const std::string &queryString(const std::string &fmt) {
|
|
|
|
|
return fmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
template<typename List, typename Fmt, typename... Args>
|
|
|
|
|
int64_t queryList(int64_t &rowId, std::vector<List> &ret, Fmt &&fmt, Args &&...arg) {
|
|
|
|
|
check();
|
|
|
|
|
auto tmp = queryString(std::forward<Fmt>(fmt), std::forward<Args>(arg)...);
|
|
|
|
|
if (doQuery(tmp)) {
|
|
|
|
|
throw SqlException(tmp, mysql_error(&_sql));
|
|
|
|
|
}
|
|
|
|
|
ret.clear();
|
|
|
|
|
MYSQL_RES *res = mysql_store_result(&_sql);
|
|
|
|
|
if (!res) {
|
|
|
|
|
rowId = mysql_insert_id(&_sql);
|
|
|
|
|
return mysql_affected_rows(&_sql);
|
|
|
|
|
}
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned int column = mysql_num_fields(res);
|
|
|
|
|
while ((row = mysql_fetch_row(res)) != nullptr) {
|
|
|
|
|
ret.emplace_back();
|
|
|
|
|
auto &back = ret.back();
|
|
|
|
|
for (unsigned int i = 0; i < column; i++) {
|
|
|
|
|
back.emplace_back(row[i] ? row[i] : "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mysql_free_result(res);
|
|
|
|
|
rowId = mysql_insert_id(&_sql);
|
|
|
|
|
return mysql_affected_rows(&_sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void check() {
|
|
|
|
|
if (mysql_ping(&_sql) != 0) {
|
|
|
|
|
throw SqlException("mysql_ping", "Mysql connection ping failed");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int doQuery(const std::string &sql) {
|
|
|
|
|
return mysql_query(&_sql, sql.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int doQuery(const char *sql) {
|
|
|
|
|
return mysql_query(&_sql, sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
MYSQL _sql;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} /* namespace toolkit */
|
|
|
|
|
#endif /* SQL_SQLCONNECTION_H_ */
|