diff --git a/orm_lib/inc/drogon/orm/SqlBinder.h b/orm_lib/inc/drogon/orm/SqlBinder.h index 0e2ab809..691ae57e 100644 --- a/orm_lib/inc/drogon/orm/SqlBinder.h +++ b/orm_lib/inc/drogon/orm/SqlBinder.h @@ -321,7 +321,7 @@ class SqlBinder else if (_type == ClientType::Mysql) { #ifdef USE_MYSQL - _format.push_back(MYSQL_TYPE_VAR_STRING); + _format.push_back(MYSQL_TYPE_STRING); #endif } return *this; @@ -344,7 +344,7 @@ class SqlBinder else if (_type == ClientType::Mysql) { #ifdef USE_MYSQL - _format.push_back(MYSQL_TYPE_VAR_STRING); + _format.push_back(MYSQL_TYPE_STRING); #endif } return *this; @@ -390,7 +390,14 @@ class SqlBinder _paraNum++; _parameters.push_back(NULL); _length.push_back(0); - _format.push_back(0); + if (_type == ClientType::PostgreSQL) + _format.push_back(0); + else if (_type == ClientType::Mysql) + { +#ifdef USE_MYSQL + _format.push_back(MYSQL_TYPE_NULL); +#endif + } return *this; } self &operator<<(const Mode &mode) diff --git a/orm_lib/src/mysql_impl/MysqlConnection.cc b/orm_lib/src/mysql_impl/MysqlConnection.cc index 5c78025d..56293554 100644 --- a/orm_lib/src/mysql_impl/MysqlConnection.cc +++ b/orm_lib/src/mysql_impl/MysqlConnection.cc @@ -13,7 +13,6 @@ #include "MysqlConnection.h" #include "MysqlResultImpl.h" -#include "MysqlStmtResultImpl.h" #include #include #include @@ -32,13 +31,6 @@ Result makeResult(const std::shared_ptr &r = std::shared_ptr(new MysqlResultImpl(r, query, affectedRows))); } -Result makeResult(const std::shared_ptr &r = std::shared_ptr(nullptr), - const std::string &query = "") -{ - return Result(std::shared_ptr(new MysqlStmtResultImpl(r, query))); -} - - } // namespace orm } // namespace drogon @@ -215,54 +207,6 @@ void MysqlConnection::handleEvent() { switch (_execStatus) { - case ExecStatus_StmtPrepare: - { - int err; - _waitStatus = mysql_stmt_prepare_cont(&err, _stmtPtr.get(), status); - if (_waitStatus == 0) - { - _execStatus = ExecStatus_None; - if (err) - { - outputStmtError(); - //FIXME exception callback; - return; - } - LOG_TRACE << "stmt ready!"; - if (mysql_stmt_bind_param(_stmtPtr.get(), _binds.data())) - { - outputStmtError(); - return; - } - _waitStatus = mysql_stmt_execute_start(&err, _stmtPtr.get()); - LOG_TRACE << "mysql_stmt_execute_start:" << _waitStatus; - _execStatus = ExecStatus_StmtExec; - if (_waitStatus == 0) - { - _execStatus = ExecStatus_None; - if (err) - { - outputStmtError(); - return; - } - - _waitStatus = mysql_stmt_store_result_start(&err, _stmtPtr.get()); - _execStatus = ExecStatus_StmtStoreResult; - if (_waitStatus == 0) - { - _execStatus = ExecStatus_None; - if (err) - { - outputStmtError(); - return; - } - getStmtResult(); - } - } - } - setChannel(); - break; - } case ExecStatus_RealQuery: { int err; @@ -316,54 +260,6 @@ void MysqlConnection::handleEvent() setChannel(); break; } - case ExecStatus_StmtExec: - { - int err; - _waitStatus = mysql_stmt_execute_cont(&err, _stmtPtr.get(), status); - LOG_TRACE << "mysql_stmt_execute_cont:" << _waitStatus; - if (_waitStatus == 0) - { - _execStatus = ExecStatus_None; - if (err) - { - outputStmtError(); - return; - } - _waitStatus = mysql_stmt_store_result_start(&err, _stmtPtr.get()); - LOG_TRACE << "mysql_stmt_store_result_start:" << _waitStatus; - _execStatus = ExecStatus_StmtStoreResult; - if (_waitStatus == 0) - { - _execStatus = ExecStatus_None; - if (err) - { - outputStmtError(); - return; - } - getStmtResult(); - } - } - setChannel(); - break; - } - case ExecStatus_StmtStoreResult: - { - int err; - _waitStatus = mysql_stmt_store_result_cont(&err, _stmtPtr.get(), status); - LOG_TRACE << "mysql_stmt_store_result_cont:" << _waitStatus; - if (_waitStatus == 0) - { - _execStatus = ExecStatus_None; - if (err) - { - outputStmtError(); - return; - } - getStmtResult(); - } - setChannel(); - break; - } default: return; } @@ -386,77 +282,72 @@ void MysqlConnection::execSql(const std::string &sql, assert(idleCb); assert(!_isWorking); assert(!sql.empty()); - _sql = sql; + _cb = rcb; _idleCb = idleCb; _isWorking = true; _exceptCb = exceptCallback; - //_channel.enableWriting(); - LOG_TRACE << sql; - // if (paraNum == 0) - // { - // _loop->runInLoop([=]() { - // int err; - // //int mysql_real_query_start(int *ret, MYSQL *mysql, const char *q, unsigned long length) - // _waitStatus = mysql_real_query_start(&err, _mysqlPtr.get(), sql.c_str(), sql.length()); - // LOG_TRACE << "real_query:" << _waitStatus; - // _execStatus = ExecStatus_RealQuery; - // if (_waitStatus == 0) - // { - // if (err) - // { - // outputError(); - // return; - // } - - // MYSQL_RES *ret; - // _waitStatus = mysql_store_result_start(&ret, _mysqlPtr.get()); - // LOG_TRACE << "store_result:" << _waitStatus; - // _execStatus = ExecStatus_StoreResult; - // if (_waitStatus == 0) - // { - // _execStatus = ExecStatus_None; - // if (!ret) - // { - // outputError(); - // return; - // } - // getResult(ret); - // } - // } - // setChannel(); - // }); - // return; - // } - - _stmtPtr = std::shared_ptr(mysql_stmt_init(_mysqlPtr.get()), [](MYSQL_STMT *stmt) { - //Is it a blocking method? - mysql_stmt_close(stmt); - }); - if (!_stmtPtr) + _sql.clear(); + if (paraNum > 0) { - outputError(); - return; + std::string::size_type pos = 0; + std::string::size_type seekPos = std::string::npos; + for (size_t i = 0; i < paraNum; i++) + { + seekPos = sql.find("?", pos); + if (seekPos == std::string::npos) + { + _sql.append(sql.substr(pos)); + break; + } + else + { + _sql.append(sql.substr(pos, seekPos - pos)); + pos = seekPos + 1; + switch (format[i]) + { + case MYSQL_TYPE_TINY: + _sql.append(std::to_string(*((char *)parameters[i]))); + break; + case MYSQL_TYPE_SHORT: + _sql.append(std::to_string(*((short *)parameters[i]))); + break; + case MYSQL_TYPE_LONG: + _sql.append(std::to_string(*((int32_t *)parameters[i]))); + break; + case MYSQL_TYPE_LONGLONG: + _sql.append(std::to_string(*((int64_t *)parameters[i]))); + break; + case MYSQL_TYPE_NULL: + _sql.append("NULL"); + break; + case MYSQL_TYPE_STRING: + { + _sql.append("'"); + std::string to(length[i] * 2, '\0'); + auto len = mysql_real_escape_string(_mysqlPtr.get(), (char *)to.c_str(), parameters[i], length[i]); + to.resize(len); + _sql.append(to); + _sql.append("'"); + } + break; + default: + break; + } + } + } } - - my_bool flag = 1; - mysql_stmt_attr_set(_stmtPtr.get(), STMT_ATTR_UPDATE_MAX_LENGTH, &flag); - - _binds.resize(paraNum); - _lengths.resize(paraNum); - _isNulls.resize(paraNum); - memset(_binds.data(), 0, sizeof(MYSQL_BIND) * paraNum); - for (size_t i = 0; i < paraNum; i++) + else { - _binds[i].buffer = (void *)parameters[i]; - _binds[i].buffer_type = (enum_field_types)format[i]; - _binds[i].buffer_length = length[i]; - _binds[i].length = (length[i] == 0 ? 0 : (_lengths[i] = length[i], &_lengths[i])); - _binds[i].is_null = (parameters[i] != NULL ? 0 : (_isNulls[i] = true, &_isNulls[i])); + _sql = sql; } + LOG_TRACE << _sql; _loop->runInLoop([=]() { int err; - _waitStatus = mysql_stmt_prepare_start(&err, _stmtPtr.get(), sql.c_str(), sql.length()); + //int mysql_real_query_start(int *ret, MYSQL *mysql, const char *q, unsigned long length) + _waitStatus = mysql_real_query_start(&err, _mysqlPtr.get(), _sql.c_str(), _sql.length()); + LOG_TRACE << "real_query:" << _waitStatus; + _execStatus = ExecStatus_RealQuery; if (_waitStatus == 0) { if (err) @@ -464,11 +355,25 @@ void MysqlConnection::execSql(const std::string &sql, outputError(); return; } - } - _execStatus = ExecStatus_StmtPrepare; + MYSQL_RES *ret; + _waitStatus = mysql_store_result_start(&ret, _mysqlPtr.get()); + LOG_TRACE << "store_result:" << _waitStatus; + _execStatus = ExecStatus_StoreResult; + if (_waitStatus == 0) + { + _execStatus = ExecStatus_None; + if (!ret) + { + outputError(); + return; + } + getResult(ret); + } + } setChannel(); }); + return; } void MysqlConnection::outputError() @@ -502,36 +407,6 @@ void MysqlConnection::outputError() } } -void MysqlConnection::outputStmtError() -{ - _channelPtr->disableAll(); - LOG_ERROR << "Error(" - << mysql_stmt_errno(_stmtPtr.get()) << ") [" << mysql_stmt_sqlstate(_stmtPtr.get()) << "] \"" - << mysql_stmt_error(_stmtPtr.get()) << "\""; - if (_isWorking) - { - try - { - //FIXME exception type - throw SqlError(mysql_stmt_error(_stmtPtr.get()), - _sql); - } - catch (...) - { - _exceptCb(std::current_exception()); - _exceptCb = decltype(_exceptCb)(); - } - - _cb = decltype(_cb)(); - _isWorking = false; - if (_idleCb) - { - _idleCb(); - _idleCb = decltype(_idleCb)(); - } - } -} - void MysqlConnection::getResult(MYSQL_RES *res) { auto resultPtr = std::shared_ptr(res, [](MYSQL_RES *r) { @@ -548,18 +423,3 @@ void MysqlConnection::getResult(MYSQL_RES *res) _idleCb = decltype(_idleCb)(); } } - -void MysqlConnection::getStmtResult() -{ - LOG_TRACE << "Got " << mysql_stmt_num_rows(_stmtPtr.get()) << " rows"; - auto Result = makeResult(_stmtPtr, _sql); - if (_isWorking) - { - _cb(Result); - _cb = decltype(_cb)(); - _exceptCb = decltype(_exceptCb)(); - _isWorking = false; - _idleCb(); - _idleCb = decltype(_idleCb)(); - } -} \ No newline at end of file diff --git a/orm_lib/src/mysql_impl/MysqlConnection.h b/orm_lib/src/mysql_impl/MysqlConnection.h index 227c4cc5..e32cc5db 100644 --- a/orm_lib/src/mysql_impl/MysqlConnection.h +++ b/orm_lib/src/mysql_impl/MysqlConnection.h @@ -55,7 +55,6 @@ class MysqlConnection : public DbConnection, public std::enable_shared_from_this void handleEvent(); void setChannel(); void getResult(MYSQL_RES *res); - void getStmtResult(); int _waitStatus; enum ExecStatus { @@ -70,7 +69,6 @@ class MysqlConnection : public DbConnection, public std::enable_shared_from_this std::shared_ptr _stmtPtr; void outputError(); - void outputStmtError(); std::vector _binds; std::vector _lengths; diff --git a/orm_lib/src/mysql_impl/MysqlStmtResultImpl.cc b/orm_lib/src/mysql_impl/MysqlStmtResultImpl.cc deleted file mode 100644 index cd31284d..00000000 --- a/orm_lib/src/mysql_impl/MysqlStmtResultImpl.cc +++ /dev/null @@ -1,66 +0,0 @@ -/** - * - * MysqlStmtResultImpl.cc - * An Tao - * - * Copyright 2018, An Tao. All rights reserved. - * Use of this source code is governed by a MIT license - * that can be found in the License file. - * - * - */ - -#include "MysqlStmtResultImpl.h" -#include -#include - -using namespace drogon::orm; - -Result::size_type MysqlStmtResultImpl::size() const noexcept -{ - return _rowsNum; -} -Result::row_size_type MysqlStmtResultImpl::columns() const noexcept -{ - return _fieldNum; -} -const char *MysqlStmtResultImpl::columnName(row_size_type number) const -{ - assert(number < _fieldNum); - if (_fieldArray) - return _fieldArray[number].name; - return ""; -} -Result::size_type MysqlStmtResultImpl::affectedRows() const noexcept -{ - return _affectedRows; -} -Result::row_size_type MysqlStmtResultImpl::columnNumber(const char colName[]) const -{ - std::string col(colName); - std::transform(col.begin(), col.end(), col.begin(), tolower); - auto iter = _fieldMap.find(col); - if (iter != _fieldMap.end()) - return iter->second; - return -1; -} -const char *MysqlStmtResultImpl::getValue(size_type row, row_size_type column) const -{ - if (_rowsNum == 0 || _fieldNum == 0) - return NULL; - assert(row < _rowsNum); - assert(column < _fieldNum); - return _rows[row].first[column]; -} -bool MysqlStmtResultImpl::isNull(size_type row, row_size_type column) const -{ - return getValue(row, column) == NULL; -} -Result::field_size_type MysqlStmtResultImpl::getLength(size_type row, row_size_type column) const -{ - if (_rowsNum == 0 || _fieldNum == 0) - return 0; - assert(row < _rowsNum); - assert(column < _fieldNum); - return _rows[row].second[column]; -} diff --git a/orm_lib/src/mysql_impl/MysqlStmtResultImpl.h b/orm_lib/src/mysql_impl/MysqlStmtResultImpl.h deleted file mode 100644 index 699fdadb..00000000 --- a/orm_lib/src/mysql_impl/MysqlStmtResultImpl.h +++ /dev/null @@ -1,133 +0,0 @@ -/** - * - * MysqlStmtResultImpl.h - * An Tao - * - * Copyright 2018, An Tao. All rights reserved. - * Use of this source code is governed by a MIT license - * that can be found in the License file. - * - * - */ -#pragma once - -#include "../ResultImpl.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace drogon -{ -namespace orm -{ - -class MysqlStmtResultImpl : public ResultImpl -{ - public: - MysqlStmtResultImpl(const std::shared_ptr &r, const std::string &query) noexcept - : _result(r), - _metaData(mysql_stmt_result_metadata(r.get()), [](MYSQL_RES *p) { - if (p) - mysql_free_result(p); }), - _query(query), - _rowsNum(r ? mysql_stmt_num_rows(r.get()) : 0), - _fieldArray(_metaData ? mysql_fetch_fields(_metaData.get()) : nullptr), - _fieldNum(_metaData ? mysql_num_fields(_metaData.get()) : 0), - _affectedRows(r ? mysql_stmt_affected_rows(r.get()) : 0) - { - MYSQL_BIND binds[_fieldNum]; - memset(binds, 0, sizeof(MYSQL_BIND) * _fieldNum); - unsigned long lengths[_fieldNum]; - my_bool isNulls[_fieldNum]; - std::shared_ptr buffers[_fieldNum]; - char fakeBuf; - if (_fieldNum > 0) - { - for (row_size_type i = 0; i < _fieldNum; i++) - { - std::string fieldName = _fieldArray[i].name; - std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), tolower); - _fieldMap[fieldName] = i; - //LOG_TRACE << "row[" << fieldName << "].max_length=" << _fieldArray[i].max_length; - if (_rowsNum > 0) - { - if (_fieldArray[i].max_length > 0) - { - buffers[i] = std::shared_ptr(new char[_fieldArray[i].max_length + 1], [](char *p) { delete[] p; }); - binds[i].buffer = buffers[i].get(); - binds[i].buffer_length = _fieldArray[i].max_length + 1; - } - else - { - binds[i].buffer = &fakeBuf; - binds[i].buffer_length = 1; - } - binds[i].length = &lengths[i]; - binds[i].is_null = &isNulls[i]; - binds[i].buffer_type = _fieldArray[i].type; - } - } - } - if (size() > 0) - { - if (mysql_stmt_bind_result(r.get(), binds)) - { - fprintf(stderr, " mysql_stmt_bind_result() failed\n"); - fprintf(stderr, " %s\n", mysql_stmt_error(r.get())); - exit(-1); - } - while (!mysql_stmt_fetch(r.get())) - { - std::vector row; - std::vector lengths; - for (row_size_type i = 0; i < _fieldNum; i++) - { - if (*(binds[i].is_null)) - { - row.push_back(NULL); - lengths.push_back(0); - } - else - { - auto data = std::shared_ptr(new char[*(binds[i].length)], [](char *p) { delete[] p; }); - memcpy(data.get(), binds[i].buffer, *binds[i].length); - _resultData.push_back(data); - row.push_back(data.get()); - lengths.push_back(*(binds[i].length)); - } - } - _rows.push_back(std::make_pair(row, lengths)); - } - } - } - virtual size_type size() const noexcept override; - virtual row_size_type columns() const noexcept override; - virtual const char *columnName(row_size_type Number) const override; - virtual size_type affectedRows() const noexcept override; - virtual row_size_type columnNumber(const char colName[]) const override; - virtual const char *getValue(size_type row, row_size_type column) const override; - virtual bool isNull(size_type row, row_size_type column) const override; - virtual field_size_type getLength(size_type row, row_size_type column) const override; - - private: - const std::shared_ptr _result; - const std::shared_ptr _metaData; - const std::string _query; - const Result::size_type _rowsNum; - const MYSQL_FIELD *_fieldArray; - const Result::row_size_type _fieldNum; - const size_type _affectedRows; - std::unordered_map _fieldMap; - std::vector, std::vector>> _rows; - - std::vector> _resultData; -}; - -} // namespace orm -} // namespace drogon diff --git a/orm_lib/src/mysql_impl/test/test1.cc b/orm_lib/src/mysql_impl/test/test1.cc index 3f355a7b..2e58fd7d 100644 --- a/orm_lib/src/mysql_impl/test/test1.cc +++ b/orm_lib/src/mysql_impl/test/test1.cc @@ -40,20 +40,27 @@ int main() }; LOG_TRACE << "end"; LOG_TRACE << "begin"; - *clientPtr << "select * from users where id!=? order by id" + *clientPtr << "select * from users where id=? and user_id=? order by id" << 139 + << "233" << Mode::Blocking >> [](const Result &r) { std::cout << "rows:" << r.size() << std::endl; std::cout << "column num:" << r.columns() << std::endl; - // for (auto row : r) - // { - // std::cout << "user_id=" << row["user_id"].as() << " id=" << row["id"].as() << std::endl; - // } + for (auto row : r) + { + std::cout << "user_id=" << row["user_id"].as() << " id=" << row["id"].as(); + std::cout << " time=" << row["time"].as() << std::endl; + } } >> [](const DrogonDbException &e) { std::cerr << e.base().what() << std::endl; }; + *clientPtr << "update users set time=? where id>?" << trantor::Date::date() << 1000 >> [](const Result &r) { + std::cout << "update " << r.affectedRows() << " rows" << std::endl; + } >> [](const DrogonDbException &e) { + std::cerr << e.base().what() << std::endl; + }; LOG_TRACE << "end"; getchar(); } \ No newline at end of file