diff --git a/install_test/example.cpp b/install_test/example.cpp index 5a1771f..27910ed 100644 --- a/install_test/example.cpp +++ b/install_test/example.cpp @@ -20,13 +20,16 @@ #include #include #include +#include +#include +#include #include "mariadb/conncpp.hpp" int main(int argc, char** argv) { - // Instantiate Driver - sql::Driver* driver = sql::mariadb::get_driver_instance(); + // Instantiate Driver + sql::Driver* driver = sql::mariadb::get_driver_instance(); // Configure Connection sql::SQLString url("jdbc:mariadb://localhost/test"); diff --git a/src/ClientSidePreparedStatement.cpp b/src/ClientSidePreparedStatement.cpp index b71ecfe..b464e58 100644 --- a/src/ClientSidePreparedStatement.cpp +++ b/src/ClientSidePreparedStatement.cpp @@ -162,7 +162,7 @@ namespace mariadb void ClientSidePreparedStatement::addBatch() { std::vector holder(prepareResult->getParamCount()); - for (int32_t i= 0; i error( @@ -182,8 +182,6 @@ namespace mariadb void ClientSidePreparedStatement::clearBatch() { parameterList.clear(); - hasLongData= false; - this->parameters.clear(); // clear() doesn't change capacity } /** {inheritdoc}. */ @@ -454,6 +452,7 @@ namespace mariadb { parameters.clear(); parameters.assign(prepareResult->getParamCount(), Shared::ParameterHolder()); + hasLongData= false; } void ClientSidePreparedStatement::close() diff --git a/src/Results.cpp b/src/Results.cpp index c46c352..2ccdad0 100644 --- a/src/Results.cpp +++ b/src/Results.cpp @@ -38,6 +38,7 @@ namespace mariadb Results::Results() : statement(nullptr) , serverPrepResult(nullptr) + , rewritten(false) { this->fetchSize= 0; this->maxFieldSize= 0; @@ -95,6 +96,7 @@ namespace mariadb , resultSetConcurrency(resultSetConcurrency) , autoIncrement(autoIncrement) , autoGeneratedKeys(autoGeneratedKeys) + , rewritten(false) { //this->cmdInformation diff --git a/src/ServerSidePreparedStatement.cpp b/src/ServerSidePreparedStatement.cpp index 99d6ecf..aa8205c 100644 --- a/src/ServerSidePreparedStatement.cpp +++ b/src/ServerSidePreparedStatement.cpp @@ -186,7 +186,6 @@ namespace sql void ServerSidePreparedStatement::clearBatch() { queryParameters.clear(); - hasLongData= false; } ParameterMetaData* ServerSidePreparedStatement::getParameterMetaData() @@ -348,6 +347,8 @@ namespace sql void ServerSidePreparedStatement::clearParameters() { currentParameterHolder.clear(); + //currentParameterHolder.assign(serverPrepareResult->getParamCount(), Shared::ParameterHolder()); + hasLongData= false; } diff --git a/src/com/CmdInformationBatch.cpp b/src/com/CmdInformationBatch.cpp index c2b2e63..14c4eae 100644 --- a/src/com/CmdInformationBatch.cpp +++ b/src/com/CmdInformationBatch.cpp @@ -104,14 +104,15 @@ namespace mariadb batchRes.reserve(std::max(updateCounts.size(), expectedSize)); - size_t pos= 0; + size_t pos= updateCounts.size(); for (auto& updCnt : updateCounts) { - batchRes[pos++]= static_cast(updCnt); + batchRes.push_back(static_cast(updCnt)); } while (pos < expectedSize) { - batchRes[pos++]= Statement::EXECUTE_FAILED; + batchRes.push_back(Statement::EXECUTE_FAILED); + ++pos; } return batchRes; @@ -122,10 +123,9 @@ namespace mariadb { batchRes.clear(); batchRes.reserve(updateCounts.size()); - size_t pos= 0; for (auto& updCnt : updateCounts) { - batchRes[pos++]= static_cast(updCnt); + batchRes.push_back(static_cast(updCnt)); } return batchRes; } @@ -159,13 +159,14 @@ namespace mariadb largeBatchRes.reserve(std::max(updateCounts.size(), expectedSize)); - size_t pos= 0; + size_t pos= updateCounts.size(); for (auto& updCnt : updateCounts) { - largeBatchRes[pos++] = updCnt; + largeBatchRes.push_back(updCnt); } while (pos < expectedSize) { - largeBatchRes[pos++]= Statement::EXECUTE_FAILED; + largeBatchRes.push_back(Statement::EXECUTE_FAILED); + ++pos; } return largeBatchRes; diff --git a/src/com/RowProtocol.cpp b/src/com/RowProtocol.cpp index 9a27ad0..5e08778 100644 --- a/src/com/RowProtocol.cpp +++ b/src/com/RowProtocol.cpp @@ -65,6 +65,18 @@ namespace mariadb #endif + long double RowProtocol::stringToDouble(const char* str, uint32_t len) + { + std::string doubleAsString(str, len); + std::istringstream convStream(doubleAsString); + std::locale C("C"); + long double result; + convStream.imbue(C); + convStream >> result; + + return result; + } + RowProtocol::RowProtocol(uint32_t _maxFieldSize, Shared::Options options) : maxFieldSize(_maxFieldSize) , options(options) diff --git a/src/com/RowProtocol.h b/src/com/RowProtocol.h index 01cafa2..af9ac93 100644 --- a/src/com/RowProtocol.h +++ b/src/com/RowProtocol.h @@ -80,6 +80,7 @@ public: static std::regex dateRegex; static std::regex timeRegex; static std::regex timestampRegex; + static long double stringToDouble(const char* str, uint32_t len); protected: static int32_t NULL_LENGTH_ ; /*-1*/ diff --git a/src/protocol/capi/QueryProtocol.cpp b/src/protocol/capi/QueryProtocol.cpp index f55b737..a81d9ca 100644 --- a/src/protocol/capi/QueryProtocol.cpp +++ b/src/protocol/capi/QueryProtocol.cpp @@ -924,27 +924,27 @@ namespace capi std::vector>& parametersList, bool hasLongData) { - + bool needToRelease= false; cmdPrologue(); if (options->useBulkStmts && !hasLongData && results->getAutoGeneratedKeys()==Statement::NO_GENERATED_KEYS && versionGreaterOrEqual(10,2,7) - && executeBulkBatch(results, sql, serverPrepareResult, parametersList)){ + && executeBulkBatch(results, sql, serverPrepareResult, parametersList)) { return true; } - if (!options->useBatchMultiSend){ + if (!options->useBatchMultiSend) { return false; } initializeBatchReader(); capi::MYSQL_STMT *stmt= nullptr; - if (serverPrepareResult == nullptr) - { + if (serverPrepareResult == nullptr) { serverPrepareResult= prepare(sql, true); + needToRelease= true; } stmt= serverPrepareResult->getStatementId(); @@ -952,14 +952,13 @@ namespace capi std::size_t totalExecutionNumber= parametersList.size(); //std::size_t parameterCount= serverPrepareResult->getParameters().size(); - for (auto& paramset : parametersList) - { + for (auto& paramset : parametersList) { executePreparedQuery(true, serverPrepareResult, results, paramset); } - //TODO:!!! what to do with serverPrepareResult here? delete? leaking otherwise. but I don't think we can do that here - delete serverPrepareResult; - + if (needToRelease) { + delete serverPrepareResult; + } return true; } diff --git a/src/protocol/capi/TextRowProtocolCapi.cpp b/src/protocol/capi/TextRowProtocolCapi.cpp index d178ade..3b1e549 100644 --- a/src/protocol/capi/TextRowProtocolCapi.cpp +++ b/src/protocol/capi/TextRowProtocolCapi.cpp @@ -370,6 +370,8 @@ namespace capi } std::ostringstream timestamp; + std::locale C("C"); + timestamp.imbue(C); timestamp << timestampsPart[0] << "-"; timestamp << (timestampsPart[1] < 10 ? "0" : "") << timestampsPart[1] << "-"; @@ -933,7 +935,7 @@ namespace capi case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_LONGLONG: try { - return std::stold(std::string(fieldBuf.arr + pos, length)); + return stringToDouble(fieldBuf.arr + pos, length); } // Common parent for std::invalid_argument and std::out_of_range catch (std::logic_error& nfe) { diff --git a/test/unit/bugs/bugs.cpp b/test/unit/bugs/bugs.cpp index 51b4b72..18f453a 100644 --- a/test/unit/bugs/bugs.cpp +++ b/test/unit/bugs/bugs.cpp @@ -34,6 +34,7 @@ #include #include #include +#include //#include "driver/mysql_error.h" //Prevent windows min() macro because of std::numeric_limits::min() @@ -1100,11 +1101,11 @@ void bugs::bug22292073() SKIP("Server does not support tested functionality(JSON type)") } stmt->execute("DROP TABLE IF EXISTS bug22292073"); - stmt->execute("create table bug22292073 (jdoc JSON);" ); + stmt->execute("create table bug22292073 (jdoc JSON);"); stmt->execute("insert into bug22292073 values('{ \"name\": \"abc\", \"age\": 1 , \"misc\":\ - 1.2}'), ('{ \"name\": \"abcdef\", \"age\": 31 , \"misc\": 1.237843}');" ); - pstmt.reset( con->prepareStatement("select JSON_EXTRACT(jdoc, '$.age') from bug22292073;") ); - res.reset( pstmt->executeQuery() ); + 1.2}'), ('{ \"name\": \"abcdef\", \"age\": 31 , \"misc\": 1.237843}');"); + pstmt.reset(con->prepareStatement("select JSON_EXTRACT(jdoc, '$.age') from bug22292073;")); + res.reset(pstmt->executeQuery()); res->next(); @@ -1149,14 +1150,14 @@ void bugs::bug22292073() void bugs::bug23212333() { - uint64_t charCount= 256*1024+1; + uint64_t charCount = 256 * 1024 + 1; stmt->executeUpdate("drop table if exists bug23212333"); stmt->executeUpdate("create table bug23212333(id longtext)"); pstmt.reset(con->prepareStatement("insert into bug23212333 VALUES(?)")); std::string buffer; - buffer.append(charCount,'A'); + buffer.append(charCount, 'A'); pstmt->setString(1, buffer); pstmt->execute(); @@ -1175,12 +1176,12 @@ void bugs::bug17227390() { std::locale::global(std::locale("fr_CA.UTF-8")); - for (int i=0; i < 2; ++i) + for (int i = 0; i < 2; ++i) { if (i == 0) { - pstmt.reset( con->prepareStatement("select 1.001 as number;") ); - res.reset( pstmt->executeQuery() ); + pstmt.reset(con->prepareStatement("select 1.001 as number;")); + res.reset(pstmt->executeQuery()); } else { @@ -1191,7 +1192,6 @@ void bugs::bug17227390() ASSERT_EQUALS(1.001L, res->getDouble(1)); ASSERT_EQUALS(1.001L, res->getDouble("number")); - } } catch (...) { @@ -1529,8 +1529,8 @@ void bugs::concpp62() res.reset(stmt->executeQuery("SELECT ts, TIME(ts) from concpp62")); ASSERT(res->next()); - ASSERT_EQUALS(res->getString(1), "2015-01-20 16:14:36.709649"); - ASSERT_EQUALS(res->getString(2), "16:14:36.709649"); + ASSERT_EQUALS("2015-01-20 16:14:36.709649", res->getString(1)); + ASSERT_EQUALS("16:14:36.709649", res->getString(2)); stmt->execute("DROP TABLE IF EXISTS concpp62"); } diff --git a/test/unit/classes/preparedstatement.cpp b/test/unit/classes/preparedstatement.cpp index fcdff08..2092323 100644 --- a/test/unit/classes/preparedstatement.cpp +++ b/test/unit/classes/preparedstatement.cpp @@ -1595,5 +1595,66 @@ void preparedstatement::executeQuery() } } + +void preparedstatement::addBatch() +{ + stmt->executeUpdate("DROP TABLE IF EXISTS testAddBatchPs"); + stmt->executeUpdate("CREATE TABLE testAddBatchPs " + "(id int not NULL)"); + + pstmt.reset(con->prepareStatement("INSERT INTO testAddBatchPs VALUES(?)")); + pstmt->setInt(1, 1); + pstmt->addBatch(); + pstmt->setInt(1, 2); + pstmt->addBatch(); + pstmt->setInt(1, 3); + pstmt->addBatch(); + + const sql::Ints& batchRes = pstmt->executeBatch(); + + res.reset(stmt->executeQuery("SELECT MIN(id), MAX(id), SUM(id), count(*) FROM testAddBatchPs")); + + ASSERT(res->next()); + + ASSERT_EQUALS(1, res->getInt(1)); + ASSERT_EQUALS(3, res->getInt(2)); + ASSERT_EQUALS(6, res->getInt(3)); + ASSERT_EQUALS(3, res->getInt(4)); + ASSERT_EQUALS(3ULL, static_cast(batchRes.size())); + ASSERT_EQUALS(1, batchRes[0]); + ASSERT_EQUALS(1, batchRes[1]); + ASSERT_EQUALS(1, batchRes[2]); + + ////// The same, but for executeLargeBatch + stmt->executeUpdate("DELETE FROM testAddBatchPs"); + + pstmt->clearBatch(); + pstmt->clearParameters(); + + pstmt->setInt(1, 4); + pstmt->addBatch(); + pstmt->setInt(1, 5); + pstmt->addBatch(); + pstmt->setInt(1, 6); + pstmt->addBatch(); + + const sql::Longs& batchLRes = pstmt->executeLargeBatch(); + + res.reset(stmt->executeQuery("SELECT MIN(id), MAX(id), SUM(id), count(*) FROM testAddBatchPs")); + + ASSERT(res->next()); + + ASSERT_EQUALS(4, res->getInt(1)); + ASSERT_EQUALS(6, res->getInt(2)); + ASSERT_EQUALS(15, res->getInt(3)); + ASSERT_EQUALS(3, res->getInt(4)); + ASSERT_EQUALS(3ULL, static_cast(batchLRes.size())); + ASSERT_EQUALS(1LL, batchLRes[0]); + ASSERT_EQUALS(1LL, batchLRes[1]); + ASSERT_EQUALS(1LL, batchLRes[2]); + + stmt->executeUpdate("DROP TABLE testAddBatchPs"); +} + } /* namespace preparedstatement */ } /* namespace testsuite */ diff --git a/test/unit/classes/preparedstatementtest.h b/test/unit/classes/preparedstatementtest.h index 2b139cc..837b8d1 100644 --- a/test/unit/classes/preparedstatementtest.h +++ b/test/unit/classes/preparedstatementtest.h @@ -68,6 +68,7 @@ public: TEST_CASE(getWarnings); TEST_CASE(blob); TEST_CASE(executeQuery); + TEST_CASE(addBatch); } /** @@ -142,7 +143,7 @@ public: */ void executeQuery(); - + void addBatch(); }; REGISTER_FIXTURE(preparedstatement); diff --git a/test/unit/classes/resultset.cpp b/test/unit/classes/resultset.cpp index b570c49..7add7f1 100644 --- a/test/unit/classes/resultset.cpp +++ b/test/unit/classes/resultset.cpp @@ -501,7 +501,22 @@ void resultset::getTypes() } else { - ASSERT_EQUALS(res->getInt(1), res->getInt(id)); + try + { + ASSERT_EQUALS(res->getInt(1), res->getInt(id)); + } + catch (sql::SQLException & e) + { + if (!isNumber) + { + ASSERT_EQUALS(1264, e.getErrorCode()); + ASSERT_EQUALS("22003", e.getSQLState()); + } + else + { + throw e; + } + } } try @@ -542,7 +557,7 @@ void resultset::getTypes() } res->first(); - if (it->is_negative || !inUintRange) + if (!isNumber || it->is_negative || !inUintRange) { try { @@ -602,7 +617,22 @@ void resultset::getTypes() // intValue >= 0 means it's in int64 range if (it->is_negative || intValue >= 0) { - ASSERT_EQUALS(res->getInt64(id), res->getInt64(1)); + try + { + ASSERT_EQUALS(res->getInt64(id), res->getInt64(1)); + } + catch (sql::SQLException & e) + { + if (!isNumber) + { + ASSERT_EQUALS(1264, e.getErrorCode()); + ASSERT_EQUALS("22003", e.getSQLState()); + } + else + { + throw e; + } + } } try { @@ -642,7 +672,7 @@ void resultset::getTypes() } res->first(); - if (it->is_negative) + if (it->is_negative || !isNumber) { try { @@ -720,7 +750,7 @@ void resultset::getTypes() } } - if (inIntRange && (pres->getInt(id) != res->getInt(id))) + if (isNumber && inIntRange && (pres->getInt(id) != res->getInt(id))) { msg.str(""); msg << "... \t\tWARNING - getInt(), PS: '" << pres->getInt(id) << "'"; @@ -729,7 +759,7 @@ void resultset::getTypes() got_warning=true; } - if (!it->is_negative && (pres->getUInt64(id) != res->getUInt64(id))) + if (isNumber && !it->is_negative && (pres->getUInt64(id) != res->getUInt64(id))) { msg.str(""); msg << "... \t\tWARNING - getUInt64(), PS: '" << pres->getUInt64(id) << "'"; @@ -738,12 +768,12 @@ void resultset::getTypes() got_warning=true; } - if (inUintRange) + if (isNumber && inUintRange) { ASSERT_EQUALS(pres->getUInt(id), res->getUInt(id)); } - if ((it->is_negative || intValue >= 0) && pres->getInt64(id) != res->getInt64(id)) + if (isNumber && (it->is_negative || intValue >= 0) && pres->getInt64(id) != res->getInt64(id)) { msg.str(""); msg << "... \t\tWARNING - getInt64(), PS: '" << pres->getInt64(id) << "'"; @@ -753,7 +783,7 @@ void resultset::getTypes() } // ASSERT_EQUALS(pres->getInt64(id), res->getInt64(id)); - if (!it->is_negative && (pres->getUInt64(id) != res->getUInt64(id))) + if (isNumber && !it->is_negative && (pres->getUInt64(id) != res->getUInt64(id))) { msg.str(""); msg << "... \t\tWARNING - getUInt64(), PS: '" << pres->getUInt64(id) << "'"; diff --git a/test/unit/classes/statement.cpp b/test/unit/classes/statement.cpp index 6a948fa..ba5b3a0 100644 --- a/test/unit/classes/statement.cpp +++ b/test/unit/classes/statement.cpp @@ -639,9 +639,31 @@ void statement::addBatch() ASSERT_EQUALS(6, res->getInt(3)); ASSERT_EQUALS(3, res->getInt(4)); ASSERT_EQUALS(3ULL, static_cast(batchRes.size())); - ASSERT_EQUALS(static_cast(sql::Statement::SUCCESS_NO_INFO), batchRes[0]); - ASSERT_EQUALS(static_cast(sql::Statement::SUCCESS_NO_INFO), batchRes[1]); - ASSERT_EQUALS(static_cast(sql::Statement::SUCCESS_NO_INFO), batchRes[2]); + ASSERT_EQUALS(1, batchRes[0]); + ASSERT_EQUALS(1, batchRes[1]); + ASSERT_EQUALS(1, batchRes[2]); + + ////// The same, but for executeLargeBatch + st2->executeUpdate("DELETE FROM testAddBatch"); + stmt->clearBatch(); + stmt->addBatch("INSERT INTO testAddBatch VALUES(4),(11)"); + stmt->addBatch("INSERT INTO testAddBatch VALUES(5)"); + stmt->addBatch("INSERT INTO testAddBatch VALUES(6)"); + + const sql::Longs& batchLRes = stmt->executeLargeBatch(); + + res.reset(st2->executeQuery("SELECT MIN(id), MAX(id), SUM(id), count(*) FROM testAddBatch")); + + ASSERT(res->next()); + + ASSERT_EQUALS(4, res->getInt(1)); + ASSERT_EQUALS(11, res->getInt(2)); + ASSERT_EQUALS(26, res->getInt(3)); + ASSERT_EQUALS(4, res->getInt(4)); + ASSERT_EQUALS(3ULL, static_cast(batchLRes.size())); + ASSERT_EQUALS(2LL, batchLRes[0]); + ASSERT_EQUALS(1LL, batchLRes[1]); + ASSERT_EQUALS(1LL, batchLRes[2]); stmt->executeUpdate("DROP TABLE IF EXISTS testAddBatch"); }