// // SQLExecutor.cpp // // Copyright (c) 2008, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #include #include #include "Poco/CppUnit/TestCase.h" #include "SQLExecutor.h" #include "Poco/NumberParser.h" #include "Poco/String.h" #include "Poco/Format.h" #include "Poco/Tuple.h" #include "Poco/DateTime.h" #include "Poco/Any.h" #include "Poco/Exception.h" #include "Poco/Data/LOB.h" #include "Poco/Data/Date.h" #include "Poco/Data/Time.h" #include "Poco/Data/StatementImpl.h" #include "Poco/Data/RecordSet.h" #include "Poco/Data/Transaction.h" #include "Poco/Data/MySQL/Connector.h" #include "Poco/Data/MySQL/MySQLException.h" #if __cplusplus >= 201103L #include #endif #if POCO_MSVS_VERSION == 2015 #define HAVE_STRUCT_TIMESPEC #endif #if (POCO_OS == POCO_OS_CYGWIN) typedef unsigned short ushort; /* System V compatibility */ typedef unsigned int uint; /* System V compatibility */ typedef unsigned long ulong; /* System V compatibility */ #endif #include using namespace Poco::Data; using namespace Poco::Data::Keywords; using Poco::Data::MySQL::ConnectionException; using Poco::Data::MySQL::StatementException; using Poco::format; using Poco::Tuple; using Poco::DateTime; using Poco::NumberParser; using Poco::Any; using Poco::AnyCast; using Poco::DynamicAny; using Poco::NotFoundException; using Poco::InvalidAccessException; using Poco::BadCastException; using Poco::RangeException; struct Person { std::string lastName; std::string firstName; std::string address; int age; Person(){age = 0;} Person(const std::string& ln, const std::string& fn, const std::string& adr, int a):lastName(ln), firstName(fn), address(adr), age(a) { } bool operator==(const Person& other) const { return lastName == other.lastName && firstName == other.firstName && address == other.address && age == other.age; } bool operator < (const Person& p) const { if (age < p.age) return true; if (lastName < p.lastName) return true; if (firstName < p.firstName) return true; return (address < p.address); } const std::string& operator () () const /// This method is required so we can extract data to a map! { // we choose the lastName as examplary key return lastName; } }; namespace Poco { namespace Data { template <> class TypeHandler { public: static void bind(std::size_t pos, const Person& obj, AbstractBinder::Ptr pBinder, AbstractBinder::Direction dir) { // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3)) poco_assert_dbg (!pBinder.isNull()); pBinder->bind(pos++, obj.lastName, dir); pBinder->bind(pos++, obj.firstName, dir); pBinder->bind(pos++, obj.address, dir); pBinder->bind(pos++, obj.age, dir); } static void prepare(std::size_t pos, const Person& obj, AbstractPreparator::Ptr pPrepare) { // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3)) poco_assert_dbg (!pPrepare.isNull()); pPrepare->prepare(pos++, obj.lastName); pPrepare->prepare(pos++, obj.firstName); pPrepare->prepare(pos++, obj.address); pPrepare->prepare(pos++, obj.age); } static std::size_t size() { return 4; } static void extract(std::size_t pos, Person& obj, const Person& defVal, AbstractExtractor::Ptr pExt) { poco_assert_dbg (!pExt.isNull()); if (!pExt->extract(pos++, obj.lastName)) obj.lastName = defVal.lastName; if (!pExt->extract(pos++, obj.firstName)) obj.firstName = defVal.firstName; if (!pExt->extract(pos++, obj.address)) obj.address = defVal.address; if (!pExt->extract(pos++, obj.age)) obj.age = defVal.age; } private: TypeHandler(); ~TypeHandler(); TypeHandler(const TypeHandler&); TypeHandler& operator=(const TypeHandler&); }; } } // namespace Poco::Data SQLExecutor::SQLExecutor(const std::string& name, Poco::Data::Session* pSession): CppUnit::TestCase(name), _pSession(pSession) { } SQLExecutor::~SQLExecutor() { } void SQLExecutor::bareboneMySQLTest(const std::string& host, const std::string& user, const std::string& pwd, const std::string& db, const std::string& port, const char* tableCreateString) { MYSQL* hsession = mysql_init(0); poco_assert (hsession != 0); MYSQL* tmp = mysql_real_connect(hsession, host.c_str(), user.c_str(), pwd.c_str(), db.c_str(), NumberParser::parse(port), 0, 0); poco_assert (tmp == hsession); MYSQL_STMT* hstmt = mysql_stmt_init(hsession); poco_assert (hstmt != 0); std::string sql = "DROP TABLE Test"; mysql_real_query(hsession, sql.c_str(), static_cast(sql.length())); sql = tableCreateString; int rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast(sql.length())); poco_assert (rc == 0); rc = mysql_stmt_execute(hstmt); poco_assert (rc == 0); sql = "INSERT INTO Test VALUES (?,?,?,?,?)"; rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast(sql.length())); poco_assert (rc == 0); std::string str[3] = { "111", "222", "333" }; int fourth = 4; float fifth = 1.5; MYSQL_BIND bind_param[5] = {{0}}; bind_param[0].buffer = const_cast(str[0].c_str()); bind_param[0].buffer_length = static_cast(str[0].length()); bind_param[0].buffer_type = MYSQL_TYPE_STRING; bind_param[1].buffer = const_cast(str[1].c_str()); bind_param[1].buffer_length = static_cast(str[1].length()); bind_param[1].buffer_type = MYSQL_TYPE_STRING; bind_param[2].buffer = const_cast(str[2].c_str()); bind_param[2].buffer_length = static_cast(str[2].length()); bind_param[2].buffer_type = MYSQL_TYPE_STRING; bind_param[3].buffer = &fourth; bind_param[3].buffer_type = MYSQL_TYPE_LONG; bind_param[4].buffer = &fifth; bind_param[4].buffer_type = MYSQL_TYPE_FLOAT; rc = mysql_stmt_bind_param(hstmt, bind_param); poco_assert (rc == 0); rc = mysql_stmt_execute(hstmt); poco_assert (rc == 0); sql = "SELECT * FROM Test"; rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast(sql.length())); poco_assert (rc == 0); char chr[3][5] = {{ 0 }}; unsigned long lengths[5] = { 0 }; fourth = 0; fifth = 0.0f; MYSQL_BIND bind_result[5] = {{0}}; bind_result[0].buffer = chr[0]; bind_result[0].buffer_length = sizeof(chr[0]); bind_result[0].buffer_type = MYSQL_TYPE_STRING; bind_result[0].length = &lengths[0]; bind_result[1].buffer = chr[1]; bind_result[1].buffer_length = sizeof(chr[1]); bind_result[1].buffer_type = MYSQL_TYPE_STRING; bind_result[1].length = &lengths[1]; bind_result[2].buffer = chr[2]; bind_result[2].buffer_length = sizeof(chr[2]); bind_result[2].buffer_type = MYSQL_TYPE_STRING; bind_result[2].length = &lengths[2]; bind_result[3].buffer = &fourth; bind_result[3].buffer_type = MYSQL_TYPE_LONG; bind_result[3].length = &lengths[3]; bind_result[4].buffer = &fifth; bind_result[4].buffer_type = MYSQL_TYPE_FLOAT; bind_result[4].length = &lengths[4]; rc = mysql_stmt_bind_result(hstmt, bind_result); poco_assert (rc == 0); rc = mysql_stmt_execute(hstmt); poco_assert (rc == 0); rc = mysql_stmt_fetch(hstmt); poco_assert (rc == 0); poco_assert (0 == std::strncmp("111", chr[0], 3)); poco_assert (0 == std::strncmp("222", chr[1], 3)); poco_assert (0 == std::strncmp("333", chr[2], 3)); poco_assert (4 == fourth); poco_assert (1.5 == fifth); rc = mysql_stmt_close(hstmt); poco_assert (rc == 0); sql = "DROP TABLE Test"; rc = mysql_real_query(hsession, sql.c_str(), static_cast(sql.length())); poco_assert (rc == 0); mysql_close(hsession); } void SQLExecutor::simpleAccess() { std::string funct = "simpleAccess()"; std::string lastName = "lastName"; std::string firstName("firstName"); std::string address("Address"); int age = 133132; int count = 0; std::string result; count = 0; try { Statement stmt(*_pSession); stmt << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(age);//, now; stmt.execute(); } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 1); try { *_pSession << "SELECT LastName FROM Person", into(result), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (lastName == result); try { *_pSession << "SELECT Age FROM Person", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == age); } void SQLExecutor::complexType() { std::string funct = "complexType()"; Person p1("LN1", "FN1", "ADDR1", 1); Person p2("LN2", "FN2", "ADDR2", 2); try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(p1), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(p2), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } int count = 0; try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 2); Person c1; Person c2; try { *_pSession << "SELECT * FROM Person WHERE LastName = 'LN1'", into(c1), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (c1 == p1); } void SQLExecutor::simpleAccessVector() { std::string funct = "simpleAccessVector()"; std::vector lastNames; std::vector firstNames; std::vector addresses; std::vector ages; std::string tableName("Person"); lastNames.push_back("LN1"); lastNames.push_back("LN2"); firstNames.push_back("FN1"); firstNames.push_back("FN2"); addresses.push_back("ADDR1"); addresses.push_back("ADDR2"); ages.push_back(1); ages.push_back(2); int count = 0; std::string result; try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 2); std::vector lastNamesR; std::vector firstNamesR; std::vector addressesR; std::vector agesR; try { *_pSession << "SELECT * FROM Person", into(lastNamesR), into(firstNamesR), into(addressesR), into(agesR), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (ages == agesR); poco_assert (lastNames == lastNamesR); poco_assert (firstNames == firstNamesR); poco_assert (addresses == addressesR); } void SQLExecutor::complexTypeVector() { std::string funct = "complexTypeVector()"; std::vector people; people.push_back(Person("LN1", "FN1", "ADDR1", 1)); people.push_back(Person("LN2", "FN2", "ADDR2", 2)); try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } int count = 0; try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 2); std::vector result; try { *_pSession << "SELECT * FROM Person", into(result), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (result == people); } void SQLExecutor::insertVector() { std::string funct = "insertVector()"; std::vector str; str.push_back("s1"); str.push_back("s2"); str.push_back("s3"); str.push_back("s3"); int count = 100; { Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(str))); try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 0); try { stmt.execute(); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 4); } count = 0; try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 4); } void SQLExecutor::insertEmptyVector() { std::string funct = "insertEmptyVector()"; std::vector str; try { *_pSession << "INSERT INTO Strings VALUES (?)", use(str), now; fail("empty collections should not work"); } catch (Poco::Exception&) { } } void SQLExecutor::insertSingleBulk() { std::string funct = "insertSingleBulk()"; int x = 0; Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(x))); for (x = 0; x < 100; ++x) { std::size_t i = stmt.execute(); poco_assert (i == 1); } int count = 0; try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 100); try { *_pSession << "SELECT SUM(str) FROM Strings", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == ((0+99)*100/2)); } void SQLExecutor::unsignedInts() { std::string funct = "unsignedInts()"; Poco::UInt32 data = std::numeric_limits::max(); Poco::UInt32 ret = 0; try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } int count = 0; try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 1); try { *_pSession << "SELECT str FROM Strings", into(ret), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (ret == data); } void SQLExecutor::floats() { std::string funct = "floats()"; float data = 1.5f; float ret = 0.0f; try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } int count = 0; try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 1); try { *_pSession << "SELECT str FROM Strings", into(ret), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (ret == data); } void SQLExecutor::doubles() { std::string funct = "floats()"; double data = 1.5; double ret = 0.0; try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } int count = 0; try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 1); try { *_pSession << "SELECT str FROM Strings", into(ret), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (ret == data); } void SQLExecutor::any() { std::string funct = "any()"; Any i8 = Poco::Int8(42); Any i16 = Poco::Int16(420); Any i32 = Poco::UInt32(42058); Any i64 = Poco::Int64(2205861); Any f = float(42.5); Any d = double(4278.5); Any s = std::string("42"); Any date = Date(DateTime()); Any t = Time(DateTime()); Any dateTime = DateTime(2017, 9, 2, 18, 49, 15); Any e; assert (e.empty()); try { *_pSession << "INSERT INTO Anys VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, null)", use(i8), use(i16), use(i32), use(i64), use(f), use(d), use(s), use(s), use(date), use(t), use(dateTime), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } int count = 0; try { *_pSession << "SELECT COUNT(*) FROM Anys", into(count), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } poco_assert (count == 1); i8 = Poco::Int8(0); i16 = Poco::Int16(0); i32 = Poco::Int32(0); i64 = Poco::Int64(0); f = 0.0f; d = 0.0; s = std::string(""); Any s2 = std::string(""); Any dateR = Date(); Any tR = Time(); Any dateTimeR = DateTime(2010, 5, 25); e = 1; assert (!e.empty()); try { *_pSession << "SELECT * FROM Anys", into(i8), into(i16), into(i32), into(i64), into(f), into(d), into(s), into(s2), into(dateR), into(tR), into(dateTimeR), into(e), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } assert (42 == AnyCast(i8)); assert (420 == AnyCast(i16)); assert (42058 == AnyCast(i32)); assert (2205861 == AnyCast(i64)); assert (42.5f == AnyCast(f)); assert (4278.5 == AnyCast(d)); assert ("42" == AnyCast(s)); assert ("42" == AnyCast(s2)); assert (AnyCast(date) == AnyCast(dateR)); assert (AnyCast