Implement typed execute, typed response, clear for unnecessary requests, delete incomplete SQL_QA type, etc.

generic
marcelb 1 month ago
parent 6201fe0225
commit 6129774980
  1. 43
      .vscode/settings.json
  2. 34
      README.md
  3. 15
      lib/core_orm.hpp
  4. 117
      lib/mysql.hpp
  5. 119
      lib/sqlqa.hpp
  6. 86
      src/mysql.cpp
  7. 97
      src/sqlqa.cpp
  8. 63
      test/test.cpp
  9. BIN
      test/test.o

@ -18,6 +18,47 @@
"thread": "cpp",
"chrono": "cpp",
"sstream": "cpp",
"map": "cpp"
"map": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"atomic": "cpp",
"bit": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdint": "cpp",
"list": "cpp",
"unordered_map": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"ratio": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"numbers": "cpp",
"semaphore": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp"
}
}

@ -9,8 +9,8 @@ A small framework for basic MySQL database operations via MySQL/Connector++
- Object oriented
- Active connection pool
- Retries to connect
- Native C++ containers: map, vector
- QA object
- Native C++ containers: vector, tuple
- Response object
- Thread safe
- Exceptions
@ -35,26 +35,24 @@ using namespace marcelb;
/**
* Init
*/
mySQL mydb("tcp://192.168.2.10:3306", "user_nm", "passss", "my_db", 5);
MySQL mydb("tcp://192.168.2.10:3306", "user_nm", "passss", "my_db", 5);
/**
* Use
*/
sqlQA test_qa;
// build qa
test_qa.select().from("records").where("enabled = 1");
try {
// execute
mydb.exec(test_qa);
* Use ------------------
*/ | |
------------------ |
try { | | | |
// execute | | | |
auto response = mydb.exec<int,string>("SELECT id,domain FROM records WHERE enabled = 0;");
// response is MySQL_Res<int,string> type
// check is execute
if (test_qa.executed) {
// print
test_qa.print(true);
// access to first result of column id
cout << test_qa.result["id"][0] << endl;
// num of returned rows and columns
cout << test_qa.num_rows << " " << test_qa.num_columns << endl;
cout << response.affected << " " << response.have_result << endl;
cout << response.rows << " " << response.columns << endl;
for (auto row : response) { // row is tuple<int,string> type
cout << get<0>(row) << " " << get<1>(row) << endl;
}
} catch (const string err) {
cout << err << endl;
}

@ -0,0 +1,15 @@
#ifndef _MYSQL_CORE_ORM_
#define _MYSQL_CORE_ORM_
using namespace std;
namespace marcelb {
/**
* Implementiraj klase za ORM koje će se ekstendati
*/
}
#endif

@ -5,8 +5,10 @@
#include <mutex>
#include <thread>
#include <future>
#include "sqlqa.hpp"
// #include <iostream>
#include <string>
#include <vector>
#include <tuple>
#include <mysql_driver.h>
#include <mysql_connection.h>
@ -25,7 +27,59 @@ using namespace mysql;
namespace marcelb {
class mySQL {
/**
* A class for creating sql responses
*/
template<typename... Types>
class MySQL_Res : public vector<tuple<Types...>> {
public:
bool have_result = false;
uint16_t affected = 0;
uint16_t rows = 0;
uint16_t columns = (int)tuple_size<tuple<Types...>>::value;
};
/**
* Type conversion functions
*/
template<typename T>
T getValue(ResultSet* res, int column);
template<>
inline int getValue<int>(ResultSet* res, int column) {
return res->getInt(column);
}
template<>
inline uint getValue<uint>(ResultSet* res, int column) {
return res->getUInt(column);
}
template<>
inline int64_t getValue<int64_t>(ResultSet* res, int column) {
return res->getInt64(column);
}
template<>
inline uint64_t getValue<uint64_t>(ResultSet* res, int column) {
return res->getUInt64(column);
}
template<>
inline float getValue<float>(ResultSet* res, int column) {
return res->getDouble(column);
}
template<>
inline double getValue<double>(ResultSet* res, int column) {
return res->getDouble(column);
}
template<>
inline string getValue<string>(ResultSet* res, int column) {
return res->getString(column);
}
template<>
inline bool getValue<bool>(ResultSet* res, int column) {
return res->getBoolean(column);
}
// implementiraj neku c++ kompatibilnu pretvorbu za timestampe, time, date datetime itd.
class MySQL {
mutex io;
MySQL_Driver *drv;
deque<Connection*> con;
@ -35,11 +89,6 @@ class mySQL {
bool runBot = true;
future<void> bot;
/**
* Get column names for a table
*/
void getColumns(const string _table, vector<string> &_columns, Connection *ptr_con); // privatno
/**
* Open one database
*/
@ -60,15 +109,24 @@ class mySQL {
*/
Connection* shift_con();
/**
* Function parses a parameterized row
*/
template<typename... Types, size_t... Is>
static tuple<Types...> getRow(sql::ResultSet* res, index_sequence<Is...>) {
return make_tuple(getValue<Types>(res, Is + 1)...);
}
public:
/**
* mySQL constructor,
* MySQL constructor,
* receive the path to the mysql server,
* username, password, database name,
* and number of active connections (optional)
*/
mySQL(const string _path, const string _username, const string _password, const string _db, const uint _available = 1);
MySQL(const string _path, const string _username, const string _password, const string _db, const uint _available = 1);
/**
* Disconnect all connections to server
@ -82,15 +140,48 @@ class mySQL {
void reconnectTrys(const uint _trys);
/**
* Execute SQLQA
* Execute the SQL statement
*/
void exec(sqlQA &sql_qa);
template<typename... Types>
MySQL_Res<Types...> exec(const string& sql_q) {
Connection* con_ptr = shift_con();
MySQL_Res<Types...> result;
try {
Statement *stmt;
stmt = con_ptr->createStatement();
result.have_result = stmt->execute(sql_q);
if (result.have_result) {
ResultSet* res = stmt->getResultSet();
result.rows = res->rowsCount();
while (res->next()) {
result.push_back(MySQL::getRow<Types...>(res, std::make_index_sequence<sizeof...(Types)>{}));
}
res->close();
delete res;
} else {
result.affected = stmt->getUpdateCount();
}
stmt->close();
delete stmt;
disconnect_one(con_ptr);
} catch (sql::SQLException& e) {
throw runtime_error(e.what());
}
return result;
}
/**
* Destruktor
* close all connections
*/
~mySQL();
~MySQL();
};

@ -1,119 +0,0 @@
#ifndef _SQLQA_
#define _SQLQA_
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
using namespace std;
namespace marcelb {
/**
* A class for creating sql queries and responses
*/
class sqlQA {
public:
// query variable list
// SQL Command
string cmd;
// Table name
string table;
// Columns name list
vector<string> columns;
// Query is update type
bool isUpdate = false;
// Query is select type
bool isSelect = false;
// answer
// Number of updates caught
uint updateCatch = 0;
// Executing status
bool executed = false;
// Answer
map<string, vector<string>> result;
// Number of returned rows
uint num_rows = 0;
// Number of returned columns
uint num_columns = 0;
// query methods
/**
* SELECT
* accept columns names, comma separated
* defualt *
*/
sqlQA& select(const string _select = "*");
/**
* FROM
* accept table name
*/
sqlQA& from(const string _tablename);
/**
* WHERE
* accept string sql condition
*/
sqlQA& where(const string _condition);
/**
* LIMIT
* set limit result
*/
sqlQA& limit(const uint _limit);
/**
* INSERT IN TO
* accept table name, and columns
*/
sqlQA& insertInTo(const string _tablename, const string _columns = "");
/**
* VALUES
* accept values
*/
sqlQA& values(const string _values);
/**
* UPDATE
* accept tablename for update query
*/
sqlQA& update(const string _tablename);
/**
* SET
* accept column and value pairs
*/
sqlQA& set(const string _column_value_pairs);
/**
* DELETE FROM
* accept table name
*/
sqlQA& deleteFrom(const string _table);
/**
* Print SQLQA
*/
void print(bool withDetail = false);
// intern methods
private:
/**
* Parse column names
*/
void parse_columns(const string _cloumns);
};
}
#endif

@ -1,7 +1,7 @@
#include "../lib/mysql.hpp"
marcelb::mySQL::mySQL(const string _path, const string _username, const string _password, const string _db, const uint _available) {
marcelb::MySQL::MySQL(const string _path, const string _username, const string _password, const string _db, const uint _available) {
path = _path;
username = _username;
password = _password;
@ -46,7 +46,7 @@ marcelb::mySQL::mySQL(const string _path, const string _username, const string _
}
Connection* marcelb::mySQL::create_con() {
Connection* marcelb::MySQL::create_con() {
uint trys = 0;
bool status = true;
Connection* new_con = NULL;
@ -72,7 +72,7 @@ Connection* marcelb::mySQL::create_con() {
return new_con;
}
bool marcelb::mySQL::disconnect() {
bool marcelb::MySQL::disconnect() {
io.lock();
bool status = true;
@ -84,7 +84,7 @@ bool marcelb::mySQL::disconnect() {
return status;
}
bool marcelb::mySQL::disconnect_one(Connection* con_ptr) {
bool marcelb::MySQL::disconnect_one(Connection* con_ptr) {
bool status = !con_ptr->isClosed();
if (status) {
@ -106,7 +106,7 @@ bool marcelb::mySQL::disconnect_one(Connection* con_ptr) {
return status;
}
bool marcelb::mySQL::open_one(Connection* con_ptr) {
bool marcelb::MySQL::open_one(Connection* con_ptr) {
bool status = true; // ako true greška je
uint trys = 0;
@ -134,83 +134,13 @@ bool marcelb::mySQL::open_one(Connection* con_ptr) {
* Broj pokušaja usljed povezivanja s bazom od 1 do unlimited;
*/
void marcelb::mySQL::reconnectTrys(const uint _trys) {
void marcelb::MySQL::reconnectTrys(const uint _trys) {
io.lock();
reconTrys = _trys;
io.unlock();
}
void marcelb::mySQL::exec(sqlQA &sql_qa) {
Connection* con_ptr = shift_con();
try {
vector<string> columns = sql_qa.columns;
if (columns.empty() && !sql_qa.table.empty()) {
getColumns(sql_qa.table, columns, con_ptr);
}
Statement *stmt;
stmt = con_ptr->createStatement();
if (sql_qa.isSelect) {
ResultSet *res = stmt->executeQuery(sql_qa.cmd);
sql_qa.executed = true;
uint num_raw_columns = 0;
while (res->next()) {
for (uint i=0; i<columns.size(); i++) {
sql_qa.result[columns[i]].push_back(res->getString(columns[i]));
num_raw_columns++;
}
}
res->close();
delete res;
sql_qa.num_columns = columns.size();
sql_qa.num_rows = num_raw_columns/columns.size();
}
if (sql_qa.isUpdate) {
sql_qa.updateCatch = stmt->executeUpdate(sql_qa.cmd);
sql_qa.executed = true;
}
else {
sql_qa.executed = stmt->execute(sql_qa.cmd);
}
stmt->close();
delete stmt;
disconnect_one(con_ptr);
}
catch (const SQLException &error) {
cout << error.what() << endl;
sql_qa.executed = false;
}
catch (const string error) {
throw error;
}
}
void marcelb::mySQL::getColumns(const string _table, vector<string> &_columns, Connection *ptr_con) {
Statement *stmt;
stmt = ptr_con->createStatement();
ResultSet *columnsRes = stmt->executeQuery("SHOW COLUMNS from " + _table);
while (columnsRes->next()) {
_columns.push_back(columnsRes->getString("Field"));
}
columnsRes->close();
stmt->close();
delete columnsRes;
delete stmt;
}
Connection* marcelb::mySQL::shift_con() {
Connection* marcelb::MySQL::shift_con() {
while (true) {
while(con.size()) {
io.lock();
@ -226,7 +156,7 @@ Connection* marcelb::mySQL::shift_con() {
}
}
marcelb::mySQL::~mySQL() {
marcelb::MySQL::~MySQL() {
runBot = false;
bot.get();
disconnect();

@ -1,97 +0,0 @@
#include "../lib/sqlqa.hpp"
using namespace marcelb;
sqlQA& marcelb::sqlQA::select(const string _columns) {
if (_columns != "*") {
parse_columns(_columns);
}
isSelect = true;
cmd += "SELECT " + _columns + " ";
return *this;
}
sqlQA& marcelb::sqlQA::from(const string _table) {
table = _table;
cmd += "FROM " + _table + " ";
return *this;
}
sqlQA& marcelb::sqlQA::where(const string _condition) {
cmd += "WHERE " + _condition + " ";
return *this;
}
sqlQA& marcelb::sqlQA::limit(const uint _limit) {
cmd += "LIMIT " + to_string(_limit) + " ";
return *this;
}
sqlQA& marcelb::sqlQA::insertInTo(const string _tablename, const string _columns) {
isUpdate = true;
cmd += "INSERT INTO " + _tablename;
if (_columns.empty()) {
cmd += " ";
}
else {
cmd += " (" + _columns + ") ";
}
return *this;
}
sqlQA& marcelb::sqlQA::values(const string _values) {
cmd += "VALUES (" + _values + ") ";
return *this;
}
sqlQA& marcelb::sqlQA::update(const string _table) {
isUpdate = true;
cmd += "UPDATE " + _table + " ";
return *this;
}
sqlQA& marcelb::sqlQA::set(const string _column_value_pairs) {
cmd += "SET " + _column_value_pairs + " ";
return *this;
}
sqlQA& marcelb::sqlQA::deleteFrom(const string _table) {
isUpdate = true;
cmd += "DELETE FROM " + _table + " ";
return *this;
}
void marcelb::sqlQA::print(bool withDetail) {
cout << "============================================" << endl;
for (auto i : result) {
for (auto j: i.second) {
cout << i.first << " : " << j << endl;
}
cout << "--------------------------------------------" << endl;
}
if (withDetail) {
cout << "-----------------DETAILS--------------------" << endl;
cout << "Is executed: " << (executed ? "true" : "false") << endl;
cout << "Update catch: " << updateCatch << endl;
cout << "Num of rows: " << num_rows << endl;
cout << "Num of columns: " << num_columns << endl;
}
cout << "============================================" << endl;
}
void marcelb::sqlQA::parse_columns(const string _columns) {
istringstream iss(_columns);
string columnName;
while (getline(iss, columnName, ',')) {
size_t startPos = columnName.find_first_not_of(" ");
size_t endPos = columnName.find_last_not_of(" ");
if (startPos != string::npos && endPos != string::npos) {
columns.push_back(columnName.substr(startPos, endPos - startPos + 1));
}
}
}

@ -10,10 +10,10 @@ using namespace marcelb;
int main() {
try {
// mySQL mydb("tcp://192.168.2.10:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", 1);
mySQL mydb("tcp://192.168.2.10:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", 5);
// MySQL mydb("tcp://192.168.2.10:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", 1);
MySQL mydb("tcp://192.168.2.10:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", 5);
// sleep(3600*10);
sleep(2);
auto start = high_resolution_clock::now();
@ -100,36 +100,47 @@ int main() {
// one by one
try {
sqlQA test_qa;
test_qa.select().from("records").where("enabled = 1");
mydb.exec(test_qa);
test_qa.print(true);
} catch (const string err) {
cout << err << endl;
}
// sqlQA test_qa;
// test_qa.select("id,domain").from("records").where("enabled = 1");
// cout << test_qa.cmd << endl;
// mydb.exec(test_qa);
// test_qa.print(true);
sleep(20);
try {
sqlQA test_qa;
test_qa.select().from("users");
mydb.exec(test_qa);
test_qa.print(true);
} catch (const string err) {
cout << err << endl;
}
auto response = mydb.exec<int,string>("SELECT id,domain FROM records WHERE enabled = 0;");
// auto response = mydb.exec<int,string>("UPDATE records SET enabled = 1;");
cout << response.affected << " " << response.have_result << endl;
cout << response.rows << " " << response.columns << endl;
sleep(20);
for (auto row : response) {
cout << get<0>(row) << " " << get<1>(row) << endl;
}
try {
sqlQA test_qa;
test_qa.select("zone_id,record_type,enabled").from("records").where("domain = 'bitelex.test'");
mydb.exec(test_qa);
test_qa.print(true);
} catch (const string err) {
cout << err << endl;
}
// sleep(20);
// try {
// sqlQA test_qa;
// test_qa.select().from("users");
// mydb.exec(test_qa);
// test_qa.print(true);
// } catch (const string err) {
// cout << err << endl;
// }
// sleep(20);
// try {
// sqlQA test_qa;
// test_qa.select("zone_id,record_type,enabled").from("records").where("domain = 'bitelex.test'");
// mydb.exec(test_qa);
// test_qa.print(true);
// } catch (const string err) {
// cout << err << endl;
// }
auto end = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(end - start);

Binary file not shown.
Loading…
Cancel
Save