From 2f4604c33f90082b05bbcfc8babbe5928297e070 Mon Sep 17 00:00:00 2001 From: Rastocny Karol Date: Fri, 11 Nov 2022 15:32:09 +0100 Subject: [PATCH 1/3] FIX: PostgeSQL support in ActiveRecord --- ActiveRecord/Compiler/src/CodeGenerator.cpp | 4 ++-- ActiveRecord/Compiler/src/HeaderGenerator.cpp | 11 ++++++++--- ActiveRecord/Compiler/src/ImplGenerator.cpp | 14 ++++++-------- ActiveRecord/Compiler/src/Parser.cpp | 6 ++++-- ActiveRecord/Compiler/src/Parser.h | 1 + ActiveRecord/Compiler/src/Types.h | 1 + .../include/Poco/ActiveRecord/ActiveRecord.h | 18 ++++++++++-------- Data/PostgreSQL/src/SessionImpl.cpp | 3 ++- 8 files changed, 34 insertions(+), 24 deletions(-) diff --git a/ActiveRecord/Compiler/src/CodeGenerator.cpp b/ActiveRecord/Compiler/src/CodeGenerator.cpp index 79f0145d9f..130fc51092 100644 --- a/ActiveRecord/Compiler/src/CodeGenerator.cpp +++ b/ActiveRecord/Compiler/src/CodeGenerator.cpp @@ -68,13 +68,13 @@ void CodeGenerator::writeHeaderComment(const std::string& fileName) const void CodeGenerator::writeInclude(const std::string& nameSpace, const std::string& name) const { - _stream << "#include \""; + _stream << "#include <"; auto ns = splitNameSpace(nameSpace); for (const auto& s: ns) { _stream << s << '/'; } - _stream << name << ".h\"\n"; + _stream << name << ".h>\n"; } diff --git a/ActiveRecord/Compiler/src/HeaderGenerator.cpp b/ActiveRecord/Compiler/src/HeaderGenerator.cpp index 915741bff2..9f13a446a4 100644 --- a/ActiveRecord/Compiler/src/HeaderGenerator.cpp +++ b/ActiveRecord/Compiler/src/HeaderGenerator.cpp @@ -33,10 +33,13 @@ void HeaderGenerator::generate() const writeHeaderComment(_class.name + ".h"); std::string guard = includeGuard(_class.nameSpace, _class.name); stream() + << "#pragma once\n" << "#ifndef " << guard << "\n" << "#define " << guard << "\n" << "\n\n"; - stream() << "#include \"Poco/ActiveRecord/ActiveRecord.h\"\n"; + stream() << "#include \n"; + if (!_class.dllExport.empty()) + writeInclude(_class.nameSpace, "Config"s); for (const auto& ref: _class.references) { writeInclude(_class.nameSpace, ref); @@ -71,7 +74,10 @@ std::string HeaderGenerator::includeGuard(const std::string& nameSpace, const st void HeaderGenerator::writeClass() const { - stream() << "class " << _class.name << ": public "; + stream() << "class "; + if (!_class.dllExport.empty()) + stream() << _class.dllExport << " "; + stream() << _class.name << ": public "; if (_class.key.empty()) stream() << "Poco::ActiveRecord::KeylessActiveRecord"; else @@ -235,7 +241,6 @@ void HeaderGenerator::writeVariables() const void HeaderGenerator::writeGetter(const Property& property) const { stream() << "\t" << paramType(property) << " " << property.name << "() const;\n"; - } diff --git a/ActiveRecord/Compiler/src/ImplGenerator.cpp b/ActiveRecord/Compiler/src/ImplGenerator.cpp index 3a550cba90..fb265d15af 100644 --- a/ActiveRecord/Compiler/src/ImplGenerator.cpp +++ b/ActiveRecord/Compiler/src/ImplGenerator.cpp @@ -30,6 +30,7 @@ ImplGenerator::ImplGenerator(const std::string& source, std::ostream& stream, co void ImplGenerator::generate() const { + stream() << "#include \"pch.h\"\n\n"; writeHeaderComment(_class.name + ".cpp"); writeInclude(_class.nameSpace, _class.name); @@ -37,7 +38,7 @@ void ImplGenerator::generate() const { if (keyType(_class) == "Poco::UUID") { - stream() << "#include \"Poco/UUIDGenerator.h\"\n"; + stream() << "#include \n"; } } @@ -224,7 +225,7 @@ void ImplGenerator::writeInsert() const << "\t\t<< \"INSERT INTO " << _class.table << " ("; bool needComma = false; - if (!_class.key.empty()) + if (!_class.key.empty() && !_class.autoIncrementID) { stream() << keyProperty(_class).column; needComma = true; @@ -245,12 +246,9 @@ void ImplGenerator::writeInsert() const << "\t\t<< \" VALUES ("; needComma = false; - if (!_class.key.empty()) + if (!_class.key.empty() && !_class.autoIncrementID) { - if (_class.autoIncrementID) - stream() << "NULL"; - else - stream() << "\" << pSPP->next() << \""; + stream() << "\" << pSPP->next() << \""; needComma = true; } @@ -276,7 +274,7 @@ void ImplGenerator::writeInsert() const if (_class.autoIncrementID) { - stream() << "\tupdateID(context()->session());\n"; + stream() << "\tupdateID(context()->session(), \"" << _class.table << "\", \"" << keyProperty(_class).column << "\"); \n"; } stream() << "}\n"; diff --git a/ActiveRecord/Compiler/src/Parser.cpp b/ActiveRecord/Compiler/src/Parser.cpp index 94401f9950..eed937b01d 100644 --- a/ActiveRecord/Compiler/src/Parser.cpp +++ b/ActiveRecord/Compiler/src/Parser.cpp @@ -103,6 +103,7 @@ void Parser::endElement(const Poco::XML::XMLString& uri, const Poco::XML::XMLStr void Parser::handleProject(const Poco::XML::Attributes& attributes) { _nameSpace = attributes.getValue("namespace"s); + _dllExport = attributes.getValue("dllexport"s); _convertCamelCase = parseBool("convertCamelCase"s, attributes.getValue("convertCamelCase"s)); } @@ -111,6 +112,7 @@ void Parser::handleClass(const Poco::XML::Attributes& attributes) { _class.name = attributes.getValue("name"s); _class.nameSpace = _nameSpace; + _class.dllExport = _dllExport; _class.table = attributes.getValue("table"s); if (_class.table.empty()) _class.table = toDatabaseName(_class.name); _class.key = attributes.getValue("key"s); @@ -231,14 +233,14 @@ char Parser::parseCardinality(const std::string& cardinality) const } -bool Parser::parseBool(const std::string& name, const std::string& value, bool deflt) const +bool Parser::parseBool(const std::string& name, const std::string& value, bool defaultValue) const { if (value == "true") return true; else if (value == "false") return false; else if (value.empty()) - return deflt; + return defaultValue; else throw Poco::InvalidArgumentException(Poco::format("%s: %s value must be 'true' or 'false'", name, where())); } diff --git a/ActiveRecord/Compiler/src/Parser.h b/ActiveRecord/Compiler/src/Parser.h index 9b639a7733..47ef917dc2 100644 --- a/ActiveRecord/Compiler/src/Parser.h +++ b/ActiveRecord/Compiler/src/Parser.h @@ -51,6 +51,7 @@ class Parser: protected Poco::XML::DefaultHandler const Poco::XML::Locator* _pLocator = nullptr; bool _convertCamelCase = false; std::string _nameSpace; + std::string _dllExport; Class _class; ClassMap _classes; std::vector _elemStack; diff --git a/ActiveRecord/Compiler/src/Types.h b/ActiveRecord/Compiler/src/Types.h index 6afb71524b..c43ecd2b53 100644 --- a/ActiveRecord/Compiler/src/Types.h +++ b/ActiveRecord/Compiler/src/Types.h @@ -43,6 +43,7 @@ struct Class { std::string name; std::string nameSpace; + std::string dllExport; std::string table; std::string key; bool autoIncrementID = false; diff --git a/ActiveRecord/include/Poco/ActiveRecord/ActiveRecord.h b/ActiveRecord/include/Poco/ActiveRecord/ActiveRecord.h index 2415d86fa4..72b75f5601 100644 --- a/ActiveRecord/include/Poco/ActiveRecord/ActiveRecord.h +++ b/ActiveRecord/include/Poco/ActiveRecord/ActiveRecord.h @@ -125,12 +125,14 @@ class ActiveRecord: public ActiveRecordBase ID& mutableID(); - void updateID(Poco::Data::Session& session); + void updateID(Poco::Data::Session& session, const std::string &table = {}, const std::string &idColumn = {}); /// Updates the ID using lastInsertID(). + /// Name of the table and the ID column is required for PostgreSQL. - static ID lastInsertID(Poco::Data::Session& session); + static ID lastInsertID(Poco::Data::Session &session, const std::string &table = {}, const std::string &idColumn = {}); /// Returns the last inserted ID from the database session. /// Used for automatically incrementing keys. + /// Name of the table and the ID column is required for PostgreSQL. template static Poco::AutoPtr withContext(Poco::AutoPtr pObj, Context::Ptr pContext) @@ -230,14 +232,14 @@ inline std::string ActiveRecord::toString() const template -void ActiveRecord::updateID(Poco::Data::Session& session) +void ActiveRecord::updateID(Poco::Data::Session& session, const std::string &table, const std::string &idColumn) { - _id = lastInsertID(session); + _id = lastInsertID(session, table, idColumn); } template -IDType ActiveRecord::lastInsertID(Poco::Data::Session& session) +IDType ActiveRecord::lastInsertID(Poco::Data::Session& session, const std::string& table, const std::string& idColumn) { using namespace Poco::Data::Keywords; @@ -249,14 +251,14 @@ IDType ActiveRecord::lastInsertID(Poco::Data::Session& session) into(id), now; } - else if (session.connector() == "PostgreSQL") + else if (session.connector() == "postgresql") { session - << "SELECT currval('id_seq')", + << "SELECT currval(pg_get_serial_sequence('" << table << "','" << idColumn << "'))", into(id), now; } - else if (session.connector() == "MySQL") + else if (session.connector() == "mysql") { session << "SELECT LAST_INSERT_ID()", diff --git a/Data/PostgreSQL/src/SessionImpl.cpp b/Data/PostgreSQL/src/SessionImpl.cpp index 778616acc5..8f8d7363bb 100644 --- a/Data/PostgreSQL/src/SessionImpl.cpp +++ b/Data/PostgreSQL/src/SessionImpl.cpp @@ -12,6 +12,7 @@ // +#include "Poco/Data/PostgreSQL/Connector.h" #include "Poco/Data/PostgreSQL/SessionImpl.h" #include "Poco/Data/PostgreSQL/PostgreSQLException.h" #include "Poco/Data/PostgreSQL/PostgreSQLStatementImpl.h" @@ -58,7 +59,7 @@ namespace PostgreSQL { SessionImpl::SessionImpl(const std::string& aConnectionString, std::size_t aLoginTimeout): Poco::Data::AbstractSessionImpl(aConnectionString, aLoginTimeout), - _connectorName("postgresql") + _connectorName(Connector::KEY) { setProperty("handle", static_cast(&_sessionHandle)); setConnectionTimeout(CONNECTION_TIMEOUT_DEFAULT); From d1c40c90a12b46982d5f830d19204eb91182b89c Mon Sep 17 00:00:00 2001 From: Rastocny Karol Date: Mon, 14 Nov 2022 07:46:05 +0100 Subject: [PATCH 2/3] ARC: Precompiled header is optional parameter --- ActiveRecord/Compiler/src/ImplGenerator.cpp | 6 ++++-- ActiveRecord/Compiler/src/Parser.cpp | 2 ++ ActiveRecord/Compiler/src/Parser.h | 1 + ActiveRecord/Compiler/src/Types.h | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ActiveRecord/Compiler/src/ImplGenerator.cpp b/ActiveRecord/Compiler/src/ImplGenerator.cpp index fb265d15af..c8526ef653 100644 --- a/ActiveRecord/Compiler/src/ImplGenerator.cpp +++ b/ActiveRecord/Compiler/src/ImplGenerator.cpp @@ -30,7 +30,9 @@ ImplGenerator::ImplGenerator(const std::string& source, std::ostream& stream, co void ImplGenerator::generate() const { - stream() << "#include \"pch.h\"\n\n"; + if (!_class.precompiledHeader.empty()) + stream() << "#include \"" << _class.precompiledHeader <<"\"\n\n"; + writeHeaderComment(_class.name + ".cpp"); writeInclude(_class.nameSpace, _class.name); @@ -274,7 +276,7 @@ void ImplGenerator::writeInsert() const if (_class.autoIncrementID) { - stream() << "\tupdateID(context()->session(), \"" << _class.table << "\", \"" << keyProperty(_class).column << "\"); \n"; + stream() << "\tupdateID(context()->session(), \"" << _class.table << "\"s, \"" << keyProperty(_class).column << "\"s); \n"; } stream() << "}\n"; diff --git a/ActiveRecord/Compiler/src/Parser.cpp b/ActiveRecord/Compiler/src/Parser.cpp index eed937b01d..aa95b392f5 100644 --- a/ActiveRecord/Compiler/src/Parser.cpp +++ b/ActiveRecord/Compiler/src/Parser.cpp @@ -104,6 +104,7 @@ void Parser::handleProject(const Poco::XML::Attributes& attributes) { _nameSpace = attributes.getValue("namespace"s); _dllExport = attributes.getValue("dllexport"s); + _precompiledHeader = attributes.getValue("precompiledHeader"s); _convertCamelCase = parseBool("convertCamelCase"s, attributes.getValue("convertCamelCase"s)); } @@ -113,6 +114,7 @@ void Parser::handleClass(const Poco::XML::Attributes& attributes) _class.name = attributes.getValue("name"s); _class.nameSpace = _nameSpace; _class.dllExport = _dllExport; + _class.precompiledHeader = _precompiledHeader; _class.table = attributes.getValue("table"s); if (_class.table.empty()) _class.table = toDatabaseName(_class.name); _class.key = attributes.getValue("key"s); diff --git a/ActiveRecord/Compiler/src/Parser.h b/ActiveRecord/Compiler/src/Parser.h index 47ef917dc2..71c59625f2 100644 --- a/ActiveRecord/Compiler/src/Parser.h +++ b/ActiveRecord/Compiler/src/Parser.h @@ -52,6 +52,7 @@ class Parser: protected Poco::XML::DefaultHandler bool _convertCamelCase = false; std::string _nameSpace; std::string _dllExport; + std::string _precompiledHeader; Class _class; ClassMap _classes; std::vector _elemStack; diff --git a/ActiveRecord/Compiler/src/Types.h b/ActiveRecord/Compiler/src/Types.h index c43ecd2b53..d6b2363c0b 100644 --- a/ActiveRecord/Compiler/src/Types.h +++ b/ActiveRecord/Compiler/src/Types.h @@ -44,6 +44,7 @@ struct Class std::string name; std::string nameSpace; std::string dllExport; + std::string precompiledHeader; std::string table; std::string key; bool autoIncrementID = false; From 3e746aeeba75b907c3130a7cb6d67c549c9e1dca Mon Sep 17 00:00:00 2001 From: Rastocny Karol Date: Mon, 14 Nov 2022 07:49:00 +0100 Subject: [PATCH 3/3] Tests: ActiveRecord tests have renenerated ORM after modifications in ARC (fixed support for PostgeSQL) --- ActiveRecord/testsuite/include/ORM/Employee.h | 5 +++-- ActiveRecord/testsuite/include/ORM/Role.h | 3 ++- ActiveRecord/testsuite/src/Employee.cpp | 4 ++-- ActiveRecord/testsuite/src/Role.cpp | 8 ++++---- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ActiveRecord/testsuite/include/ORM/Employee.h b/ActiveRecord/testsuite/include/ORM/Employee.h index 2310dbb0e4..5816cd933d 100644 --- a/ActiveRecord/testsuite/include/ORM/Employee.h +++ b/ActiveRecord/testsuite/include/ORM/Employee.h @@ -5,12 +5,13 @@ // +#pragma once #ifndef ORM_Employee_INCLUDED #define ORM_Employee_INCLUDED -#include "Poco/ActiveRecord/ActiveRecord.h" -#include "ORM/Role.h" +#include +#include namespace ORM { diff --git a/ActiveRecord/testsuite/include/ORM/Role.h b/ActiveRecord/testsuite/include/ORM/Role.h index eb86583251..bad29e926c 100644 --- a/ActiveRecord/testsuite/include/ORM/Role.h +++ b/ActiveRecord/testsuite/include/ORM/Role.h @@ -5,11 +5,12 @@ // +#pragma once #ifndef ORM_Role_INCLUDED #define ORM_Role_INCLUDED -#include "Poco/ActiveRecord/ActiveRecord.h" +#include namespace ORM { diff --git a/ActiveRecord/testsuite/src/Employee.cpp b/ActiveRecord/testsuite/src/Employee.cpp index 928f849d8a..dfabd9b2e7 100644 --- a/ActiveRecord/testsuite/src/Employee.cpp +++ b/ActiveRecord/testsuite/src/Employee.cpp @@ -5,8 +5,8 @@ // -#include "ORM/Employee.h" -#include "Poco/UUIDGenerator.h" +#include +#include using namespace std::string_literals; diff --git a/ActiveRecord/testsuite/src/Role.cpp b/ActiveRecord/testsuite/src/Role.cpp index 95b6768cb9..90a5e54bd5 100644 --- a/ActiveRecord/testsuite/src/Role.cpp +++ b/ActiveRecord/testsuite/src/Role.cpp @@ -5,7 +5,7 @@ // -#include "ORM/Role.h" +#include using namespace std::string_literals; @@ -52,11 +52,11 @@ void Role::insert() Poco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider()); context()->session() - << "INSERT INTO roles (id, name, description)" - << " VALUES (NULL, " << pSPP->next() << ", " << pSPP->next() << ")", + << "INSERT INTO roles (name, description)" + << " VALUES (" << pSPP->next() << ", " << pSPP->next() << ")", use(*this), now; - updateID(context()->session()); + updateID(context()->session(), "roles"s, "id"s); }