diff --git a/.vscode/settings.json b/.vscode/settings.json index 1c8ea32..7b6a465 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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" } } \ No newline at end of file diff --git a/README.md b/README.md index 5b0a1f8..150c2b3 100644 --- a/README.md +++ b/README.md @@ -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("SELECT id,domain FROM records WHERE enabled = 0;"); + // response is MySQL_Res 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 type + cout << get<0>(row) << " " << get<1>(row) << endl; } + } catch (const string err) { cout << err << endl; } diff --git a/lib/core_orm.hpp b/lib/core_orm.hpp new file mode 100644 index 0000000..907bab9 --- /dev/null +++ b/lib/core_orm.hpp @@ -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 \ No newline at end of file diff --git a/lib/mysql.hpp b/lib/mysql.hpp index c121a9a..edf49bc 100644 --- a/lib/mysql.hpp +++ b/lib/mysql.hpp @@ -5,8 +5,10 @@ #include #include #include - -#include "sqlqa.hpp" +// #include +#include +#include +#include #include #include @@ -25,7 +27,59 @@ using namespace mysql; namespace marcelb { -class mySQL { +/** + * A class for creating sql responses +*/ +template +class MySQL_Res : public vector> { + public: + bool have_result = false; + uint16_t affected = 0; + uint16_t rows = 0; + uint16_t columns = (int)tuple_size>::value; +}; + +/** + * Type conversion functions +*/ + +template +T getValue(ResultSet* res, int column); +template<> +inline int getValue(ResultSet* res, int column) { + return res->getInt(column); +} +template<> +inline uint getValue(ResultSet* res, int column) { + return res->getUInt(column); +} +template<> +inline int64_t getValue(ResultSet* res, int column) { + return res->getInt64(column); +} +template<> +inline uint64_t getValue(ResultSet* res, int column) { + return res->getUInt64(column); +} +template<> +inline float getValue(ResultSet* res, int column) { + return res->getDouble(column); +} +template<> +inline double getValue(ResultSet* res, int column) { + return res->getDouble(column); +} +template<> +inline string getValue(ResultSet* res, int column) { + return res->getString(column); +} +template<> +inline bool getValue(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 con; @@ -35,11 +89,6 @@ class mySQL { bool runBot = true; future bot; - /** - * Get column names for a table - */ - void getColumns(const string _table, vector &_columns, Connection *ptr_con); // privatno - /** * Open one database */ @@ -60,15 +109,24 @@ class mySQL { */ Connection* shift_con(); + /** + * Function parses a parameterized row + */ + + template + static tuple getRow(sql::ResultSet* res, index_sequence) { + return make_tuple(getValue(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 + MySQL_Res exec(const string& sql_q) { + Connection* con_ptr = shift_con(); + MySQL_Res 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(res, std::make_index_sequence{})); + } + + 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(); }; diff --git a/lib/sqlqa.hpp b/lib/sqlqa.hpp deleted file mode 100644 index bbb7451..0000000 --- a/lib/sqlqa.hpp +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef _SQLQA_ -#define _SQLQA_ - -#include -#include -#include -#include -#include - -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 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> 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 \ No newline at end of file diff --git a/src/mysql.cpp b/src/mysql.cpp index 10019a2..f3178b0 100644 --- a/src/mysql.cpp +++ b/src/mysql.cpp @@ -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 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; igetString(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 &_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(); diff --git a/src/sqlqa.cpp b/src/sqlqa.cpp deleted file mode 100644 index 24efba1..0000000 --- a/src/sqlqa.cpp +++ /dev/null @@ -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)); - } - } -} \ No newline at end of file diff --git a/test/test.cpp b/test/test.cpp index 91c364a..32fd557 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -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("SELECT id,domain FROM records WHERE enabled = 0;"); + // auto response = mydb.exec("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(end - start); diff --git a/test/test.o b/test/test.o index a871cda..6cf3a69 100755 Binary files a/test/test.o and b/test/test.o differ