added ActiveRecord library and compiler

This commit is contained in:
Günter Obiltschnig 2021-06-18 08:51:42 +02:00
parent 9dfda83305
commit 22e762dd9a
43 changed files with 4113 additions and 23 deletions

View File

@ -0,0 +1,37 @@
# Sources
file(GLOB SRCS_G "src/*.cpp")
POCO_SOURCES_AUTO(SRCS ${SRCS_G})
# Headers
file(GLOB_RECURSE HDRS_G "include/*.h")
POCO_HEADERS_AUTO(SRCS ${HDRS_G})
# Version Resource
if(MSVC AND BUILD_SHARED_LIBS)
source_group("Resources" FILES ${PROJECT_SOURCE_DIR}/DLLVersion.rc)
list(APPEND SRCS ${PROJECT_SOURCE_DIR}/DLLVersion.rc)
endif()
add_library(ActiveRecord ${SRCS})
add_library(Poco::ActiveRecord ALIAS ActiveRecord)
set_target_properties(ActiveRecord
PROPERTIES
VERSION ${SHARED_LIBRARY_VERSION} SOVERSION ${SHARED_LIBRARY_VERSION}
OUTPUT_NAME PocoActiveRecord
DEFINE_SYMBOL ActiveRecord_EXPORTS
)
target_link_libraries(ActiveRecord PUBLIC Poco::Data Poco::Foundation)
target_include_directories(ActiveRecord
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
)
POCO_INSTALL(ActiveRecord)
POCO_GENERATE_PACKAGE(ActiveRecord)
if(ENABLE_TESTS)
add_subdirectory(testsuite)
endif()

View File

@ -0,0 +1,19 @@
# Sources
file(GLOB SRCS_G "src/*.cpp")
POCO_SOURCES_AUTO(SRCS ${SRCS_G})
add_executable(ActiveRecordCompiler ${SRCS})
set_target_properties(ActiveRecordCompiler
PROPERTIES
OUTPUT_NAME arc
)
target_link_libraries(ActiveRecordCompiler PUBLIC Poco::Foundation Poco::Util)
install(
TARGETS ActiveRecordCompiler EXPORT "ActiveRecordCompiler"
LIBRARY DESTINATION lib${LIB_SUFFIX}
ARCHIVE DESTINATION lib${LIB_SUFFIX}
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)

View File

@ -0,0 +1,14 @@
#
# Makefile
#
# Makefile for Poco ActiveRecord Compiler
#
include $(POCO_BASE)/build/rules/global
objects = Parser CodeGenerator HeaderGenerator ImplGenerator Compiler
target = arc
target_libs = PocoUtil PocoJSON PocoXML PocoFoundation
include $(POCO_BASE)/build/rules/exec

View File

@ -0,0 +1,166 @@
//
// CodeGenerator.cpp
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "CodeGenerator.h"
#include "Poco/StringTokenizer.h"
#include <set>
using namespace std::string_literals;
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
CodeGenerator::CodeGenerator(const std::string& source, std::ostream& stream):
_source(source),
_stream(stream)
{
}
void CodeGenerator::writeBeginNameSpace(const std::string& nameSpace) const
{
if (!nameSpace.empty())
{
auto ns = splitNameSpace(nameSpace);
for (const auto& s: ns)
{
_stream << "namespace " << s << " {\n";
}
}
}
void CodeGenerator::writeEndNameSpace(const std::string& nameSpace) const
{
if (!nameSpace.empty())
{
auto ns = splitNameSpace(nameSpace);
for (const auto& s: ns)
{
_stream << "} ";
}
_stream << "// namespace " << nameSpace << "\n";
}
}
void CodeGenerator::writeHeaderComment(const std::string& fileName) const
{
_stream
<< "//\n"
<< "// " << fileName << "\n"
<< "//\n"
<< "// This file has been generated from " << _source << ". Do not edit.\n"
<< "//\n\n\n";
}
void CodeGenerator::writeInclude(const std::string& nameSpace, const std::string& name) const
{
_stream << "#include \"";
auto ns = splitNameSpace(nameSpace);
for (const auto& s: ns)
{
_stream << s << '/';
}
_stream << name << ".h\"\n";
}
std::vector<std::string> CodeGenerator::splitNameSpace(const std::string& nameSpace)
{
std::vector<std::string> result;
Poco::StringTokenizer tok(nameSpace, ":"s, Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY);
result.insert(result.end(), tok.begin(), tok.end());
return result;
}
std::string CodeGenerator::propertyType(const Property& prop) const
{
std::string type;
if (prop.nullable) type += "Poco::Nullable<";
type += prop.type;
if (prop.nullable) type += ">";
return type;
}
std::string CodeGenerator::paramType(const Property& prop) const
{
std::string type;
if (!prop.nullable && isSimpleType(prop.type))
{
type = propertyType(prop);
}
else
{
type += "const ";
type += propertyType(prop);
type += "&";
}
return type;
}
std::string CodeGenerator::keyType(const Class& clazz) const
{
for (const auto& p: clazz.properties)
{
if (p.name == clazz.key)
{
return propertyType(p);
}
}
return ""s;
}
bool CodeGenerator::isSimpleType(const std::string& type)
{
static const std::set<std::string> simpleTypes =
{
"bool"s,
"char"s,
"Poco::UInt8"s,
"Poco::Int8"s,
"Poco::UInt16"s,
"Poco::Int16"s,
"Poco::UInt32"s,
"Poco::Int32"s,
"Poco::UInt64"s,
"Poco::Int64"s,
"float"s,
"double"s
};
return simpleTypes.find(type) != simpleTypes.end();
}
std::string CodeGenerator::fullClassName(const Class& clazz) const
{
std::string fullName;
auto ns = splitNameSpace(clazz.nameSpace);
for (const auto& n: ns)
{
fullName += n;
fullName += "::";
}
fullName += clazz.name;
return fullName;
}
} } } // namespace Poco::ActiveRecord::Compiler

View File

@ -0,0 +1,71 @@
//
// CodeGenerator.h
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecordCompiler_CodeGenerator_INCLUDED
#define ActiveRecordCompiler_CodeGenerator_INCLUDED
#include "Types.h"
#include <string>
#include <ostream>
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
class CodeGenerator
{
public:
CodeGenerator(const std::string& source, std::ostream& stream);
static std::vector<std::string> splitNameSpace(const std::string& nameSpace);
protected:
const std::string& source() const;
std::ostream& stream() const;
void writeBeginNameSpace(const std::string& nameSpace) const;
void writeEndNameSpace(const std::string& nameSpace) const;
void writeHeaderComment(const std::string& fileName) const;
void writeInclude(const std::string& nameSpace, const std::string& name) const;
std::string propertyType(const Property& prop) const;
std::string paramType(const Property& prop) const;
std::string keyType(const Class& clazz) const;
std::string fullClassName(const Class& clazz) const;
static bool isSimpleType(const std::string& type);
private:
std::string _source;
std::ostream& _stream;
};
//
// inlines
//
inline std::ostream& CodeGenerator::stream() const
{
return _stream;
}
inline const std::string& CodeGenerator::source() const
{
return _source;
}
} } } // namespace Poco::ActiveRecord::Compiler
#endif // ActiveRecordCompiler_CodeGenerator_INCLUDED

View File

@ -0,0 +1,220 @@
//
// CompilerApp.cpp
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Util/Application.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/FileStream.h"
#include "Poco/Path.h"
#include "Poco/File.h"
#include "Parser.h"
#include "HeaderGenerator.h"
#include "ImplGenerator.h"
#include <iostream>
#include <sstream>
using namespace std::string_literals;
using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
using Poco::Util::OptionCallback;
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
class CompilerApp: public Application
{
public:
CompilerApp()
{
}
protected:
void initialize(Application& self)
{
loadConfiguration(); // load default configuration files, if present
Application::initialize(self);
}
void defineOptions(OptionSet& options)
{
Application::defineOptions(options);
options.addOption(
Option("help", "h", "Display help information on command line arguments."s)
.required(false)
.repeatable(false)
.callback(OptionCallback<CompilerApp>(this, &CompilerApp::handleHelp)));
}
void handleHelp(const std::string& name, const std::string& value)
{
_helpRequested = true;
displayHelp();
stopOptionsProcessing();
}
void displayHelp()
{
HelpFormatter helpFormatter(options());
helpFormatter.setCommand(commandName());
helpFormatter.setUsage("OPTIONS");
helpFormatter.setHeader("POCO C++ Libraries ORM Compiler");
helpFormatter.format(std::cout);
}
void resolveReferences(const Class& clazz, const ClassMap& classes)
{
for (const auto& r: clazz.references)
{
auto it = classes.find(r);
if (it == classes.end())
{
throw Poco::NotFoundException(Poco::format("class %s has a reference to unknown class %s"s, clazz.name, r));
}
else
{
const Class& refClass = it->second;
for (const auto& rr: refClass.references)
{
if (rr == clazz.name)
{
throw Poco::CircularReferenceException(Poco::format("class %s has a circular reference to class %s"s, clazz.name, refClass.name));
}
}
}
}
}
void resolveForeignKeys(const Class& clazz, const ClassMap& classes)
{
for (const auto& p: clazz.properties)
{
if (!p.foreignKey.empty())
{
auto it = classes.find(p.referencedClass);
if (it == classes.end())
{
throw Poco::NotFoundException(Poco::format("class %s has a reference to unknown class %s"s, clazz.name, p.referencedClass));
}
else
{
const Class& refClass = it->second;
bool found = false;
for (const auto& rp: refClass.properties)
{
if (rp.column == p.foreignKey)
{
found = true;
break;
}
}
if (!found) throw Poco::NotFoundException(Poco::format("class %s has a reference to unknown foreign key column %s in table %s"s, clazz.name, p.foreignKey, refClass.table));
}
}
}
}
void resolve(const ClassMap& classes)
{
for (const auto& p: classes)
{
const Class& clazz = p.second;
resolveReferences(clazz, classes);
resolveForeignKeys(clazz, classes);
}
}
void compile(const std::string& path)
{
Parser parser;
Poco::FileInputStream xmlStream(path);
ClassMap classes = parser.parse(path, xmlStream);
resolve(classes);
for (const auto& p: classes)
{
Poco::Path headerPath;
headerPath.pushDirectory("include"s);
auto ns = CodeGenerator::splitNameSpace(p.second.nameSpace);
for (const auto& n: ns)
{
headerPath.pushDirectory(n);
}
Poco::File headerDir(headerPath.toString());
headerDir.createDirectories();
headerPath.setFileName(p.first);
headerPath.setExtension("h");
Poco::FileOutputStream headerStream(headerPath.toString());
HeaderGenerator hg(path, headerStream, p.second, classes);
hg.generate();
headerStream.close();
Poco::Path implPath;
implPath.pushDirectory("src"s);
Poco::File implDir(implPath.toString());
implDir.createDirectories();
implPath.setFileName(p.first);
implPath.setExtension("cpp");
Poco::FileOutputStream implStream(implPath.toString());
ImplGenerator ig(path, implStream, p.second, classes);
ig.generate();
implStream.close();
}
}
int main(const ArgVec& args)
{
if (!_helpRequested)
{
if (args.empty())
{
displayHelp();
}
else
{
try
{
for (const auto& a: args)
{
compile(a);
}
}
catch (Poco::Exception& exc)
{
std::cout << exc.displayText() << std::endl;
}
}
}
return Application::EXIT_OK;
}
private:
bool _helpRequested = false;
};
} } } // namespace Poco::ActiveRecord::Compiler
POCO_APP_MAIN(Poco::ActiveRecord::Compiler::CompilerApp)

View File

@ -0,0 +1,351 @@
//
// HeaderGenerator.cpp
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "HeaderGenerator.h"
#include "Poco/Exception.h"
using namespace std::string_literals;
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
HeaderGenerator::HeaderGenerator(const std::string& source, std::ostream& stream, const Class& clazz, const ClassMap& classes):
CodeGenerator(source, stream),
_class(clazz),
_classes(classes)
{
}
void HeaderGenerator::generate() const
{
writeHeaderComment(_class.name + ".h");
std::string guard = includeGuard(_class.nameSpace, _class.name);
stream()
<< "#ifndef " << guard << "\n"
<< "#define " << guard << "\n"
<< "\n\n";
stream() << "#include \"Poco/ActiveRecord/ActiveRecord.h\"\n";
for (const auto& ref: _class.references)
{
writeInclude(_class.nameSpace, ref);
}
stream() << "\n\n";
writeBeginNameSpace(_class.nameSpace);
stream() << "\n\n";
writeClass();
writeEndNameSpace(_class.nameSpace);
stream() << "\n\n";
writeTypeHandler();
stream()
<< "\n\n"
<< "#endif // " << guard << "\n";
}
std::string HeaderGenerator::includeGuard(const std::string& nameSpace, const std::string& name) const
{
std::string guard;
auto ns = splitNameSpace(nameSpace);
for (const auto& s: ns)
{
guard += s;
guard += '_';
}
guard += name;
guard += "_INCLUDED";
return guard;
}
void HeaderGenerator::writeClass() const
{
stream() << "class " << _class.name << ": public ";
if (_class.key.empty())
stream() << "Poco::ActiveRecord::KeylessActiveRecord";
else
stream() << "Poco::ActiveRecord::ActiveRecord<" << keyType(_class) << ">";
stream() << "\n{\npublic:\n";
stream() << "\tusing Ptr = Poco::AutoPtr<" << _class.name << ">;\n\n";
if (!_class.key.empty())
{
stream() << "\texplicit " << _class.name << "(ID id);\n";
}
stream()
<< "\t" << _class.name << "() = default;\n"
<< "\t" << _class.name << "(const " << _class.name << "& other);\n"
<< "\t~" << _class.name << "() = default;\n"
<< "\n";
writeSimpleAccessors();
writeReferencingAccessors();
if (!_class.key.empty())
{
stream() << "\tstatic Ptr find(Poco::ActiveRecord::Context::Ptr pContext, const ID& id);\n\n";
}
stream()
<< "\tvoid insert();\n"
<< "\tvoid update();\n"
<< "\tvoid remove();\n"
<< "\n"
<< "\tstatic const std::vector<std::string>& columns();\n"
<< "\tstatic const std::string& table();\n";
stream() << "\nprivate:\n";
writeVariables();
stream() << "\n\tfriend class Poco::Data::TypeHandler<" << _class.name << ">;\n";
stream() << "};\n";
stream() << "\n\n";
writeInlineAccessorImpls();
writeInlineReferencingAccessorImpls();
}
void HeaderGenerator::writeTypeHandler() const
{
stream()
<< "namespace Poco {\n"
<< "namespace Data {\n"
<< "\n\n"
<< "template <>\n"
<< "class TypeHandler<" << fullClassName(_class) << ">\n"
<< "{\n"
<< "public:\n"
<< "\tstatic std::size_t size()\n"
<< "\t{\n"
<< "\t\treturn " << _class.properties.size() - (_class.key.empty() ? 0 : 1) << ";\n"
<< "\t}\n"
<< "\n"
<< "\tstatic void bind(std::size_t pos, const " << fullClassName(_class) << "& ar, AbstractBinder::Ptr pBinder, AbstractBinder::Direction dir)\n"
<< "\t{\n";
for (const auto& p: _class.properties)
{
if (p.name != _class.key)
{
stream() << "\t\tTypeHandler<" << propertyType(p) << ">::bind(pos++, ar._" << p.name << ", pBinder, dir);\n";
}
}
stream()
<< "}\n"
<< "\n"
<< "\tstatic void extract(std::size_t pos, " << fullClassName(_class) << "& ar, const " << fullClassName(_class) << "& deflt, AbstractExtractor::Ptr pExtr)\n"
<< "\t{\n";
for (const auto& p: _class.properties)
{
if (p.name != _class.key)
{
stream() << "\t\tTypeHandler<" << propertyType(p) << ">::extract(pos++, ar._" << p.name << ", deflt._" << p.name << ", pExtr);\n";
}
}
stream()
<< "}\n"
<< "\n"
<< "\tstatic void prepare(std::size_t pos, const " << fullClassName(_class) << "& ar, AbstractPreparator::Ptr pPrep)\n"
<< "\t{\n";
for (const auto& p: _class.properties)
{
if (p.name != _class.key)
{
stream() << "\t\tTypeHandler<" << propertyType(p) << ">::prepare(pos++, ar._" << p.name << ", pPrep);\n";
}
}
stream()
<< "\t}\n"
<< "};\n"
<< "\n\n"
<< "} } // namespace Poco::Data\n";
}
void HeaderGenerator::writeSimpleAccessors() const
{
for (const auto& p: _class.properties)
{
if (p.referencedClass.empty() && p.name != _class.key)
{
writeGetter(p);
writeSetter(p);
stream() << "\n";
}
}
}
void HeaderGenerator::writeReferencingAccessors() const
{
for (const auto& p: _class.properties)
{
if (!p.referencedClass.empty() && (p.cardinality == Property::CARD_ZERO_OR_ONE || p.cardinality == Property::CARD_ONE))
{
writeReferenceGetter(p);
writeReferenceSetter(p);
stream() << "\n";
}
}
}
void HeaderGenerator::writeVariables() const
{
for (const auto& p: _class.properties)
{
if (p.name != _class.key)
{
stream() << "\t" << propertyType(p) << " _" << p.name;
if (isSimpleType(p.type) && !p.nullable)
{
if (p.referencedClass.empty())
{
if (p.type == "bool")
stream() << " = false";
else
stream() << " = 0";
}
else
{
const Class& refClass = referencedClass(p);
stream() << " = " << refClass.name << "::INVALID_ID";
}
}
stream() << ";\n";
}
}
}
void HeaderGenerator::writeGetter(const Property& property) const
{
stream() << "\t" << paramType(property) << " " << property.name << "() const;\n";
}
void HeaderGenerator::writeSetter(const Property& property) const
{
stream() << "\t" << _class.name << "& " << property.name << "(" << paramType(property) << " value);\n";
}
void HeaderGenerator::writeReferenceGetter(const Property& property) const
{
const Class& refClass = referencedClass(property);
stream()
<< "\t" << refClass.name << "::Ptr " << property.name << "() const;\n"
<< "\t" << paramType(property) << " " << property.name << "ID() const;\n";
}
void HeaderGenerator::writeReferenceSetter(const Property& property) const
{
const Class& refClass = referencedClass(property);
stream()
<< "\t" << _class.name << "& " << property.name << "(" << refClass.name << "::Ptr pObject);\n"
<< "\t" << _class.name << "& " << property.name << "ID(" << paramType(property) << " id);\n";
}
void HeaderGenerator::writeInlineAccessorImpls() const
{
for (const auto& p: _class.properties)
{
if (p.referencedClass.empty() && p.name != _class.key)
{
writeInlineGetterImpl(p);
stream() << "\n\n";
writeInlineSetterImpl(p);
stream() << "\n\n";
}
}
}
void HeaderGenerator::writeInlineReferencingAccessorImpls() const
{
for (const auto& p: _class.properties)
{
if (!p.referencedClass.empty() && (p.cardinality == Property::CARD_ZERO_OR_ONE || p.cardinality == Property::CARD_ONE))
{
writeInlineReferencingGetterImpl(p);
stream() << "\n\n";
writeInlineReferencingSetterImpl(p);
stream() << "\n\n";
}
}
}
void HeaderGenerator::writeInlineGetterImpl(const Property& property) const
{
stream()
<< "inline " << paramType(property) << " " << _class.name << "::" << property.name << "() const\n"
<< "{\n"
<< "\treturn _" << property.name << ";\n"
<< "}\n";
}
void HeaderGenerator::writeInlineSetterImpl(const Property& property) const
{
stream()
<< "inline " << _class.name << "& " << _class.name << "::" << property.name << "(" << paramType(property) << " value)\n"
<< "{\n"
<< "\t_" << property.name << " = value;\n"
<< "\treturn *this;\n"
<< "}\n";
}
void HeaderGenerator::writeInlineReferencingGetterImpl(const Property& property) const
{
stream()
<< "inline " << paramType(property) << " " << _class.name << "::" << property.name << "ID() const\n"
<< "{\n"
<< "\treturn _" << property.name << ";\n"
<< "}\n";
}
void HeaderGenerator::writeInlineReferencingSetterImpl(const Property& property) const
{
stream()
<< "inline " << _class.name << "& " << _class.name << "::" << property.name << "ID(" << paramType(property) << " value)\n"
<< "{\n"
<< "\t_" << property.name << " = value;\n"
<< "\treturn *this;\n"
<< "}\n";
}
const Class& HeaderGenerator::referencedClass(const Property& property) const
{
poco_assert (!property.referencedClass.empty());
auto it = _classes.find(property.referencedClass);
if (it != _classes.end())
return it->second;
else
throw Poco::NotFoundException("referenced class"s, property.referencedClass);
}
} } } // namespace Poco::ActiveRecord::Compiler

View File

@ -0,0 +1,59 @@
//
// HeaderGenerator.h
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecordCompiler_HeaderGenerator_INCLUDED
#define ActiveRecordCompiler_HeaderGenerator_INCLUDED
#include "CodeGenerator.h"
#include "Types.h"
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
class HeaderGenerator: public CodeGenerator
{
public:
HeaderGenerator(const std::string& source, std::ostream& stream, const Class& clazz, const ClassMap& classes);
void generate() const;
void writeClass() const;
void writeTypeHandler() const;
void writeSimpleAccessors() const;
void writeReferencingAccessors() const;
void writeVariables() const;
void writeGetter(const Property& property) const;
void writeSetter(const Property& property) const;
void writeReferenceGetter(const Property& property) const;
void writeReferenceSetter(const Property& property) const;
void writeInlineAccessorImpls() const;
void writeInlineReferencingAccessorImpls() const;
void writeInlineGetterImpl(const Property& property) const;
void writeInlineSetterImpl(const Property& property) const;
void writeInlineReferencingGetterImpl(const Property& property) const;
void writeInlineReferencingSetterImpl(const Property& property) const;
const Class& referencedClass(const Property& property) const;
protected:
std::string includeGuard(const std::string& nameSpace, const std::string& name) const;
private:
Class _class;
const ClassMap& _classes;
};
} } } // namespace Poco::ActiveRecord::Compiler
#endif // ActiveRecordCompiler_HeaderGenerator_INCLUDED

View File

@ -0,0 +1,418 @@
//
// ImplGenerator.cpp
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "ImplGenerator.h"
#include "Poco/Exception.h"
using namespace std::string_literals;
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
ImplGenerator::ImplGenerator(const std::string& source, std::ostream& stream, const Class& clazz, const ClassMap& classes):
CodeGenerator(source, stream),
_class(clazz),
_classes(classes)
{
}
void ImplGenerator::generate() const
{
writeHeaderComment(_class.name + ".cpp");
writeInclude(_class.nameSpace, _class.name);
if (!_class.key.empty())
{
if (keyType(_class) == "Poco::UUID")
{
stream() << "#include \"Poco/UUIDGenerator.h\"\n";
}
}
stream() << "\n\n";
stream() << "using namespace std::string_literals;\n";
stream() << "using namespace Poco::Data::Keywords;\n";
stream() << "\n\n";
writeBeginNameSpace(_class.nameSpace);
stream() << "\n\n";
writeClassMembers();
writeEndNameSpace(_class.nameSpace);
}
void ImplGenerator::writeClassMembers() const
{
if (!_class.key.empty())
{
stream()
<< _class.name << "::" << _class.name << "(ID id):\n"
<< "\tPoco::ActiveRecord::ActiveRecord<" << keyType(_class) << ">(id)\n"
<< "{\n"
<< "}\n"
<< "\n\n";
}
writeCopyConstructor();
stream() << "\n\n";
writeReferencingAccessorsImpl();
if (!_class.key.empty())
{
writeFind();
stream() << "\n\n";
}
writeInsert();
stream() << "\n\n";
writeUpdate();
stream() << "\n\n";
writeRemove();
stream() << "\n\n";
writeColumns();
stream() << "\n\n";
writeTable();
stream() << "\n\n";
}
void ImplGenerator::writeCopyConstructor() const
{
stream() << _class.name << "::" << _class.name << "(const " << _class.name << "& other):\n";
bool needComma = false;
if (!_class.key.empty())
{
stream() << "\tPoco::ActiveRecord::ActiveRecord<" << keyType(_class) << ">(other)";
needComma = true;
}
for (const auto& p: _class.properties)
{
if (p.name != _class.key)
{
if (needComma) stream() << ",\n";
stream() << "\t_" << p.name << "(other._" << p.name << ")";
needComma = true;
}
}
stream()
<< "\n"
<< "{\n"
<< "}\n";
}
void ImplGenerator::writeReferencingAccessorsImpl() const
{
for (const auto& p: _class.properties)
{
if (!p.referencedClass.empty() && (p.cardinality == Property::CARD_ZERO_OR_ONE || p.cardinality == Property::CARD_ONE))
{
writeReferencingGetterImpl(p);
stream() << "\n\n";
writeReferencingSetterImpl(p);
stream() << "\n\n";
}
}
}
void ImplGenerator::writeReferencingGetterImpl(const Property& property) const
{
const Class& refClass = referencedClass(property);
stream()
<< refClass.name << "::Ptr " << _class.name << "::" << property.name << "() const\n"
<< "{\n";
if (property.nullable)
{
stream()
<< "\tif (!_" << property.name << ".isNull())\n"
<< "\t\treturn " << refClass.name << "::find(context(), _" << property.name << ".value());\n"
<< "\telse\n"
<< "\t\treturn nullptr;\n";
}
else
{
stream() << "\treturn " << refClass.name << "::find(context(), _" << property.name << ");\n";
}
stream() << "}\n";
}
void ImplGenerator::writeReferencingSetterImpl(const Property& property) const
{
const Class& refClass = referencedClass(property);
stream()
<< _class.name << "& " << _class.name << "::" << property.name << "(" << refClass.name << "::Ptr pObject)\n"
<< "{\n"
<< "\tif (pObject)\n"
<< "\t\t_" << property.name << " = pObject->id();\n"
<< "\telse\n"
<< "\t\t_" << property.name << " = " << refClass.name << "::INVALID_ID;\n"
<< "\treturn *this;\n"
<< "}\n";
}
void ImplGenerator::writeFind() const
{
stream()
<< _class.name << "::Ptr " << _class.name << "::find(Poco::ActiveRecord::Context::Ptr pContext, const ID& id)\n"
<< "{\n"
<< "\tPoco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(pContext->statementPlaceholderProvider());\n"
<< "\t" << _class.name << "::Ptr pObject(new " << _class.name << ");\n"
<< "\n"
<< "\tpContext->session()\n"
<< "\t\t<< \"SELECT "
<< keyProperty(_class).column;
for (const auto& p: _class.properties)
{
if (p.name != _class.key)
{
stream() << ", " << p.column;
}
}
stream()
<< "\"\n"
<< "\t\t<< \" FROM " << _class.table << "\"\n"
<< "\t\t<< \" WHERE " << keyProperty(_class).column << " = \" << pSPP->next(),\n"
<< "\t\tinto(pObject->mutableID()),\n"
<< "\t\tinto(*pObject),\n"
<< "\t\tbind(id),\n"
<< "\t\tnow;\n"
<< "\n"
<< "\treturn withContext(pObject, pContext);\n"
<< "}\n";
}
void ImplGenerator::writeInsert() const
{
stream()
<< "void " << _class.name << "::insert()\n"
<< "{\n"
<< "\tPoco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider());\n"
<< "\n";
if (!_class.key.empty())
{
if (keyType(_class) == "Poco::UUID")
{
stream()
<< "\tif (id().isNull())\n"
<< "\t{\n"
<< "\t\tmutableID() = Poco::UUIDGenerator().createRandom();\n"
<< "\t}\n"
<< "\n";
}
}
stream()
<< "\tcontext()->session()\n"
<< "\t\t<< \"INSERT INTO " << _class.table << " (";
bool needComma = false;
if (!_class.key.empty())
{
stream() << keyProperty(_class).column;
needComma = true;
}
for (const auto& p: _class.properties)
{
if (p.name != _class.key)
{
if (needComma) stream() << ", ";
stream() << p.column;
needComma = true;
}
}
stream()
<< ")\"\n"
<< "\t\t<< \" VALUES (";
needComma = false;
if (!_class.key.empty())
{
if (_class.autoIncrementID)
stream() << "NULL";
else
stream() << "\" << pSPP->next() << \"";
needComma = true;
}
for (const auto& p: _class.properties)
{
if (p.name != _class.key)
{
if (needComma) stream() << ", ";
stream() << "\" << pSPP->next() << \"";
needComma = true;
}
}
stream() << ")\",\n";
if (!_class.key.empty() && !_class.autoIncrementID)
{
stream() << "\t\tbind(id()),\n";
}
stream()
<< "\t\tuse(*this),\n"
<< "\t\tnow;\n";
if (_class.autoIncrementID)
{
stream() << "\tupdateID(context()->session());\n";
}
stream() << "}\n";
}
void ImplGenerator::writeUpdate() const
{
stream()
<< "void " << _class.name << "::update()\n"
<< "{\n";
if (_class.key.empty())
{
stream() << "\tthrow Poco::NotImplementedException(\"update not implemented for keyless class\", \"" << _class.name << "\");\n";
}
else
{
stream()
<< "\tPoco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider());\n"
<< "\n"
<< "\tcontext()->session()\n"
<< "\t\t<< \"UPDATE " << _class.table << "\"\n"
<< "\t\t<< \" SET ";
bool needComma = false;
for (const auto& p: _class.properties)
{
if (p.name != _class.key)
{
if (needComma) stream() << " << \", ";
stream() << p.column << " = \" << pSPP->next()";
needComma = true;
}
}
stream()
<< "\n"
<< "\t\t<< \" WHERE " << keyProperty(_class).column << " = \" << pSPP->next(),\n"
<< "\t\tuse(*this),\n"
<< "\t\tbind(id()),\n"
<< "\t\tnow;\n";
}
stream() << "}\n";
}
void ImplGenerator::writeRemove() const
{
stream()
<< "void " << _class.name << "::remove()\n"
<< "{\n"
<< "\tPoco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider());\n"
<< "\n"
<< "\tcontext()->session()\n"
<< "\t\t<< \"DELETE FROM " << _class.table << "\"\n"
<< "\t\t<< \" WHERE ";
if (_class.key.empty())
{
bool needAnd = false;
for (const auto& p: _class.properties)
{
if (needAnd) stream() << " << \" AND ";
stream() << p.column << " = \" << pSPP->next()";
needAnd = true;
}
stream()
<< ",\n"
<< "\t\tuse(*this),\n"
<< "\t\tnow;\n";
}
else
{
stream()
<< keyProperty(_class).column << " = \" << pSPP->next(),\n"
<< "\t\tbind(id()),\n"
<< "\t\tnow;\n";
}
stream() << "}\n";
}
void ImplGenerator::writeColumns() const
{
stream()
<< "const std::vector<std::string>& " << _class.name << "::columns()\n"
<< "{\n"
<< "\tstatic const std::vector<std::string> cols =\n"
<< "\t{\n";
for (const auto& p: _class.properties)
{
stream() << "\t\t\"" << p.column << "\"s,\n";
}
stream()
<< "\t};\n"
<< "\n"
<< "\treturn cols;\n"
<< "}\n";
}
void ImplGenerator::writeTable() const
{
stream()
<< "const std::string& " << _class.name << "::table()\n"
<< "{\n"
<< "\tstatic const std::string t = \"" << _class.table << "\";\n"
<< "\treturn t;\n"
<< "}\n";
}
const Property& ImplGenerator::keyProperty(const Class& clazz) const
{
for (const auto& p: clazz.properties)
{
if (p.name == clazz.key)
return p;
}
throw Poco::NotFoundException("key property"s, clazz.key);
}
const Class& ImplGenerator::referencedClass(const Property& property) const
{
poco_assert (!property.referencedClass.empty());
auto it = _classes.find(property.referencedClass);
if (it != _classes.end())
return it->second;
else
throw Poco::NotFoundException("referenced class"s, property.referencedClass);
}
} } } // namespace Poco::ActiveRecord::Compiler

View File

@ -0,0 +1,56 @@
//
// ImplGenerator.h
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecordCompiler_ImplGenerator_INCLUDED
#define ActiveRecordCompiler_ImplGenerator_INCLUDED
#include "CodeGenerator.h"
#include "Types.h"
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
class ImplGenerator: public CodeGenerator
{
public:
ImplGenerator(const std::string& source, std::ostream& stream, const Class& clazz, const ClassMap& classes);
void generate() const;
void writeClassMembers() const;
void writeCopyConstructor() const;
void writeReferencingAccessorsImpl() const;
void writeReferencingGetterImpl(const Property& property) const;
void writeReferencingSetterImpl(const Property& property) const;
void writeFind() const;
void writeInsert() const;
void writeUpdate() const;
void writeRemove() const;
void writeColumns() const;
void writeTable() const;
const Class& referencedClass(const Property& property) const;
protected:
const Property& keyProperty(const Class& clazz) const;
std::string includeGuard(const std::string& nameSpace, const std::string& name) const;
private:
Class _class;
const ClassMap& _classes;
};
} } } // namespace Poco::ActiveRecord::Compiler
#endif // ActiveRecordCompiler_ImplGenerator_INCLUDED

View File

@ -0,0 +1,277 @@
//
// Parser.cpp
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Parser.h"
#include "Poco/SAX/Attributes.h"
#include "Poco/SAX/Locator.h"
#include "Poco/SAX/InputSource.h"
#include "Poco/SAX/SAXParser.h"
#include "Poco/Format.h"
#include "Poco/Ascii.h"
#include "Poco/Exception.h"
using namespace std::string_literals;
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
Parser::Parser()
{
}
ClassMap Parser::parse(const std::string& systemId, std::istream& stream)
{
Poco::XML::SAXParser parser;
parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, false);
parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACE_PREFIXES, false);
parser.setContentHandler(this);
Poco::XML::InputSource inputSource(stream);
inputSource.setSystemId(systemId);
parser.parse(&inputSource);
poco_assert (_elemStack.empty());
return _classes;
}
void Parser::setDocumentLocator(const Poco::XML::Locator* pLocator)
{
_pLocator = pLocator;
}
void Parser::startElement(const Poco::XML::XMLString& uri, const Poco::XML::XMLString& localName, const Poco::XML::XMLString& qname, const Poco::XML::Attributes& attributes)
{
if (qname == "project")
{
if (_elemStack.empty())
{
_elemStack.push_back(qname);
handleProject(attributes);
}
else throw Poco::SyntaxException(Poco::format("%s: project element must be at document root"s, where()));
}
else if (qname == "class")
{
if (_elemStack.size() == 1 && _elemStack.back() == "project")
{
_elemStack.push_back(qname);
handleClass(attributes);
}
else throw Poco::SyntaxException(Poco::format("%s: class element must be within project element"s, where()));
}
else if (qname == "property")
{
if (_elemStack.size() == 2 && _elemStack.back() == "class")
{
_elemStack.push_back(qname);
handleProperty(attributes);
}
else throw Poco::SyntaxException(Poco::format("%s: property element must be within class element"s, where()));
}
else throw Poco::SyntaxException(Poco::format("%s: invalid element: %s"s, where(), qname));
}
void Parser::endElement(const Poco::XML::XMLString& uri, const Poco::XML::XMLString& localName, const Poco::XML::XMLString& qname)
{
poco_assert (_elemStack.size() > 0);
if (qname == "class")
{
_classes[_class.name] = _class;
}
_elemStack.pop_back();
}
void Parser::handleProject(const Poco::XML::Attributes& attributes)
{
_nameSpace = attributes.getValue("namespace"s);
_convertCamelCase = parseBool("convertCamelCase"s, attributes.getValue("convertCamelCase"s));
}
void Parser::handleClass(const Poco::XML::Attributes& attributes)
{
_class.name = attributes.getValue("name"s);
_class.nameSpace = _nameSpace;
_class.table = attributes.getValue("table"s);
if (_class.table.empty()) _class.table = toDatabaseName(_class.name);
_class.key = attributes.getValue("key"s);
_class.autoIncrementID = parseBool("autoIncrementID"s, attributes.getValue("autoIncrementID"s), false);
_class.properties.clear();
_class.references.clear();
}
void Parser::handleProperty(const Poco::XML::Attributes& attributes)
{
Property prop;
prop.name = attributes.getValue("name"s);
prop.column = attributes.getValue("column"s);
if (prop.column.empty()) prop.column = toDatabaseName(prop.name);
prop.type = parseType(attributes.getValue("type"s));
prop.referencedClass = attributes.getValue("references"s);
prop.foreignKey = attributes.getValue("foreignKey"s);
prop.cardinality = parseCardinality(attributes.getValue("cardinality"));
prop.nullable = parseBool("nullable"s, attributes.getValue("nullable"s), false);
_class.properties.push_back(prop);
if (!prop.referencedClass.empty())
{
if (prop.cardinality == Property::CARD_ZERO_OR_ONE)
{
prop.nullable = true;
}
}
if (prop.referencedClass.empty() && !prop.foreignKey.empty())
{
throw Poco::InvalidArgumentException(Poco::format("%s: foreign key specified, but no referenced class"s, where()));
}
if (!prop.referencedClass.empty() && prop.referencedClass != _class.name && std::find(_class.references.begin(), _class.references.end(), prop.referencedClass) == _class.references.end())
{
_class.references.push_back(prop.referencedClass);
}
if (_class.key.empty() && prop.name == "id")
{
_class.key = prop.name;
}
}
std::string Parser::where() const
{
if (_pLocator)
{
if (_pLocator->getSystemId().empty())
return Poco::format("Line %d, column %d"s,
_pLocator->getLineNumber(),
_pLocator->getColumnNumber());
else
return Poco::format("File '%s', line %d, column %d"s,
_pLocator->getSystemId(),
_pLocator->getLineNumber(),
_pLocator->getColumnNumber());
}
else return "<unknown>";
}
std::string Parser::parseType(const std::string& type) const
{
static const std::map<std::string, std::string> typeMap =
{
{"bool"s, "bool"s},
{"char"s, "char"s},
{"int8"s, "Poco::Int8"s},
{"uint8"s, "Poco::UInt8"s},
{"int16"s, "Poco::Int16"s},
{"uint16"s, "Poco::UInt16"s},
{"int32"s, "Poco::Int32"s},
{"uint32"s, "Poco::UInt32"s},
{"int64"s, "Poco::Int64"s},
{"uint64"s, "Poco::UInt64"s},
{"float"s, "float"s},
{"double"s, "double"s},
{"dateTime"s, "Poco::DateTime"s},
{"timestamp"s, "Poco::Timestamp"s},
{"time"s, "Poco::Data::Time"s},
{"date"s, "Poco::Data::Date"s},
{"uuid"s, "Poco::UUID"s},
{"string"s, "std::string"s}
};
auto it = typeMap.find(type);
if (it == typeMap.end())
throw Poco::InvalidArgumentException(Poco::format("%s: invalid type: %s", where(), type));
else
return it->second;
}
char Parser::parseCardinality(const std::string& cardinality) const
{
if (cardinality.empty())
{
return Property::CARD_ONE;
}
else if (cardinality.size() == 1)
{
switch (cardinality[0])
{
case Property::CARD_ZERO_OR_ONE:
case Property::CARD_ONE:
case Property::CARD_ZERO_OR_MORE:
case Property::CARD_ONE_OR_MORE:
return cardinality[0];
default:
break;
}
}
throw Poco::InvalidArgumentException(Poco::format("%s: cardinality must be one of ?, 1, *, +", where()));
}
bool Parser::parseBool(const std::string& name, const std::string& value, bool deflt) const
{
if (value == "true")
return true;
else if (value == "false")
return false;
else if (value.empty())
return deflt;
else throw Poco::InvalidArgumentException(Poco::format("%s: %s value must be 'true' or 'false'", name, where()));
}
std::string Parser::convertCamelCase(const std::string& name)
{
std::string result;
for (const char c: name)
{
if (Poco::Ascii::isUpper(c))
{
if (!(result.empty() || result.back() == '_'))
{
result += '_';
}
result += Poco::Ascii::toLower(c);
}
else
{
result += c;
}
}
return result;
}
std::string Parser::toDatabaseName(const std::string& name)
{
if (_convertCamelCase)
return convertCamelCase(name);
else
return name;
}
} } } // namespace Poco::ActiveRecord::Compiler

View File

@ -0,0 +1,63 @@
//
// Parser.h
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecordCompiler_Parser_INCLUDED
#define ActiveRecordCompiler_Parser_INCLUDED
#include "Types.h"
#include "Poco/SAX/DefaultHandler.h"
#include <istream>
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
class Parser: protected Poco::XML::DefaultHandler
/// A parser for the XML ORM (project/class/property) class specification file.
{
public:
Parser();
/// Creates the Parser.
ClassMap parse(const std::string& systemId, std::istream& stream);
/// Parses the XML file.
protected:
// ContentHandler
void setDocumentLocator(const Poco::XML::Locator* pLocator);
void startElement(const Poco::XML::XMLString& uri, const Poco::XML::XMLString& localName, const Poco::XML::XMLString& qname, const Poco::XML::Attributes& attributes);
void endElement(const Poco::XML::XMLString& uri, const Poco::XML::XMLString& localName, const Poco::XML::XMLString& qname);
void handleProject(const Poco::XML::Attributes& attributes);
void handleClass(const Poco::XML::Attributes& attributes);
void handleProperty(const Poco::XML::Attributes& attributes);
std::string where() const;
std::string parseType(const std::string& type) const;
char parseCardinality(const std::string& cardinality) const;
bool parseBool(const std::string& name, const std::string& value, bool deflt = false) const;
std::string convertCamelCase(const std::string& name);
std::string toDatabaseName(const std::string& name);
private:
const Poco::XML::Locator* _pLocator = nullptr;
bool _convertCamelCase = false;
std::string _nameSpace;
Class _class;
ClassMap _classes;
std::vector<std::string> _elemStack;
};
} } } // namespace Poco::ActiveRecord::Compiler
#endif // ActiveRecordCompiler_Parser_INCLUDED

View File

@ -0,0 +1,60 @@
//
// Types.h
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecordCompiler_Types_INCLUDED
#define ActiveRecordCompiler_Types_INCLUDED
#include "Poco/Types.h"
#include <vector>
#include <map>
namespace Poco {
namespace ActiveRecord {
namespace Compiler {
struct Property
{
static const char CARD_ZERO_OR_ONE = '?';
static const char CARD_ONE = '1';
static const char CARD_ZERO_OR_MORE = '*';
static const char CARD_ONE_OR_MORE = '+';
std::string name;
std::string column;
std::string type;
std::string referencedClass;
std::string foreignKey;
char cardinality = CARD_ONE;
bool nullable = false;
};
struct Class
{
std::string name;
std::string nameSpace;
std::string table;
std::string key;
bool autoIncrementID = false;
std::vector<Property> properties;
std::vector<std::string> references;
};
using ClassMap = std::map<std::string, Class>;
} } } // namespace Poco::ActiveRecord::Compiler
#endif // ActiveRecordCompiler_Types_INCLUDED

15
ActiveRecord/Makefile Normal file
View File

@ -0,0 +1,15 @@
#
# Makefile
#
# Makefile for Poco ActiveRecord Library
#
include $(POCO_BASE)/build/rules/global
objects = Context ActiveRecord IDTraits StatementPlaceholderProvider
target = PocoActiveRecord
target_version = 1
target_libs = PocoData PocoFoundation
include $(POCO_BASE)/build/rules/lib

View File

@ -0,0 +1,4 @@
include(CMakeFindDependencyMacro)
find_dependency(PocoFoundation)
find_dependency(PocoData)
include("${CMAKE_CURRENT_LIST_DIR}/PocoActiveRecordTargets.cmake")

View File

@ -0,0 +1,278 @@
//
// ActiveRecord.h
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: ActiveRecord
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecord_ActiveRecord_INCLUDED
#define ActiveRecord_ActiveRecord_INCLUDED
#include "Poco/ActiveRecord/ActiveRecordLib.h"
#include "Poco/ActiveRecord/Context.h"
#include "Poco/ActiveRecord/IDTraits.h"
#include "Poco/ActiveRecord/TypeHandler.h"
#include "Poco/DateTime.h"
#include "Poco/RefCountedObject.h"
#include "Poco/AutoPtr.h"
#include "Poco/Types.h"
#include <limits>
namespace Poco {
namespace ActiveRecord {
class ActiveRecordLib_API ActiveRecordBase: public Poco::RefCountedObject
/// The base class for the ActiveRecord class template.
{
public:
using Ptr = Poco::AutoPtr<ActiveRecordBase>;
virtual std::string toString() const = 0;
/// Returns a string representation of the object for
/// debugging purposes. The default implementation returns the ID.
void create(Context::Ptr pContext);
/// Attaches the given Context and calls insert().
virtual void insert() = 0;
/// Inserts a new row in the database with the content of this object.
/// The ID must be 0, and after successful insert, a unique ID
/// is assigned.
virtual void update() = 0;
/// Updates the row in the database with the content of this object.
virtual void remove() = 0;
/// Deletes the corresponding row in the database.
void attach(Context::Ptr pContext);
/// Attaches the object to a Context.
void detach();
/// Detaches the object from its Context.
Context::Ptr context() const;
/// Returns the Context this object is attached to,
/// or a null pointer if the object has not been
/// attached to a Context.
virtual bool isValid() const;
/// Returns true iff the object is valid ID, otherwise false.
bool isAttached() const;
/// Returns true iff the object has been attached to a Context, otherwise false.
protected:
ActiveRecordBase() = default;
~ActiveRecordBase() = default;
template <typename T>
static Poco::AutoPtr<T> withContext(Poco::AutoPtr<T> pObj, Context::Ptr pContext)
{
if (pObj && pObj->isValid())
{
pObj->attach(pContext);
return pObj;
}
else return nullptr;
}
private:
ActiveRecordBase(const ActiveRecordBase&) = delete;
ActiveRecordBase& operator = (const ActiveRecordBase&) = delete;
Context::Ptr _pContext;
};
template <typename IDType>
class ActiveRecord: public ActiveRecordBase
/// The base class for all database objects that
/// implement the ActiveRecord pattern, with a
/// single key column.
{
public:
using Ptr = Poco::AutoPtr<ActiveRecord>;
using ID = IDType;
static const IDType INVALID_ID;
ID id() const;
/// Returns the unique ID of the object.
// ActiveRecordBase
std::string toString() const;
bool isValid() const;
protected:
ActiveRecord() = default;
ActiveRecord(ID id): _id(id) {};
~ActiveRecord() = default;
ActiveRecord(const ActiveRecord& other):
_id(other._id)
{
}
ID& mutableID();
void updateID(Poco::Data::Session& session);
/// Updates the ID using lastInsertID().
static ID lastInsertID(Poco::Data::Session& session);
/// Returns the last inserted ID from the database session.
/// Used for automatically incrementing keys.
template <typename T>
static Poco::AutoPtr<T> withContext(Poco::AutoPtr<T> pObj, Context::Ptr pContext)
{
if (pObj->isValid())
{
pObj->attach(pContext);
return pObj;
}
else return nullptr;
}
template <typename AR>
static void queryInto(Poco::Data::Statement& statement, AR& ar)
{
statement, Poco::Data::Keywords::into(ar._id), Poco::Data::Keywords::into(ar);
}
private:
ActiveRecord& operator = (const ActiveRecord&) = delete;
ID _id = INVALID_ID;
template <typename ActRec> friend class Query;
};
template <typename IDType>
const IDType ActiveRecord<IDType>::INVALID_ID = IDTraits<IDType>::INVALID_ID;
class KeylessActiveRecord: public ActiveRecordBase
/// The base class for all database objects that
/// implement the ActiveRecord pattern, without
/// a key column.
{
public:
using Ptr = Poco::AutoPtr<KeylessActiveRecord>;
// ActiveRecordBase
std::string toString() const;
protected:
template <typename AR>
static void queryInto(Poco::Data::Statement& statement, AR& ar)
{
statement, Poco::Data::Keywords::into(ar);
}
template <typename ActRec> friend class Query;
};
//
// inlines
//
inline Context::Ptr ActiveRecordBase::context() const
{
return _pContext;
}
inline bool ActiveRecordBase::isAttached() const
{
return !_pContext.isNull();
}
template <typename IDType>
inline IDType ActiveRecord<IDType>::id() const
{
return _id;
}
template <typename IDType>
inline IDType& ActiveRecord<IDType>::mutableID()
{
return _id;
}
template <typename IDType>
inline bool ActiveRecord<IDType>::isValid() const
{
return IDTraits<IDType>::isValid(_id);
}
template <typename IDType>
inline std::string ActiveRecord<IDType>::toString() const
{
return IDTraits<IDType>::toString(_id);
}
template <typename IDType>
void ActiveRecord<IDType>::updateID(Poco::Data::Session& session)
{
_id = lastInsertID(session);
}
template <typename IDType>
IDType ActiveRecord<IDType>::lastInsertID(Poco::Data::Session& session)
{
using namespace Poco::Data::Keywords;
IDType id;
if (session.connector() == "sqlite")
{
session
<< "SELECT last_insert_rowid()",
into(id),
now;
}
else if (session.connector() == "PostgreSQL")
{
session
<< "SELECT currval('id_seq')",
into(id),
now;
}
else if (session.connector() == "MySQL")
{
session
<< "SELECT LAST_INSERT_ID()",
into(id),
now;
}
else
{
throw Poco::NotImplementedException("lastInsertID not implemented for connector", session.connector());
}
return id;
}
} } // namespace Poco::ActiveRecord
#endif // ActiveRecord_ActiveRecord_INCLUDED

View File

@ -0,0 +1,58 @@
//
// ActiveRecordLib.h
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: ActiveRecordLib
//
// Basic definitions for the ActiveRecord library.
// This file must be the first file included by every other ActiveRecordLib
// header file.
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecord_ActiveRecordLib_INCLUDED
#define ActiveRecord_ActiveRecordLib_INCLUDED
#include "Poco/Poco.h"
//
// The following block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the ActiveRecordLib_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// ActiveRecordLib_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
//
#if defined(_WIN32) && defined(POCO_DLL)
#if defined(ActiveRecordLib_EXPORTS)
#define ActiveRecordLib_API __declspec(dllexport)
#else
#define ActiveRecordLib_API __declspec(dllimport)
#endif
#endif
#if !defined(ActiveRecordLib_API)
#define ActiveRecordLib_API
#endif
//
// Automatically link ActiveRecordLib library.
//
#if defined(_MSC_VER)
#if !defined(POCO_NO_AUTOMATIC_LIBS) && !defined(ActiveRecordLib_EXPORTS)
#pragma comment(lib, "PocoActiveRecord" POCO_LIB_SUFFIX)
#endif
#endif
#endif // ActiveRecord_ActiveRecordLib_INCLUDED

View File

@ -0,0 +1,74 @@
//
// Context.h
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: Context
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecord_Context_INCLUDED
#define ActiveRecord_Context_INCLUDED
#include "Poco/ActiveRecord/ActiveRecordLib.h"
#include "Poco/ActiveRecord/StatementPlaceholderProvider.h"
#include "Poco/Data/Session.h"
#include "Poco/RefCountedObject.h"
#include "Poco/AutoPtr.h"
namespace Poco {
namespace ActiveRecord {
class ActiveRecordLib_API Context: public Poco::RefCountedObject
/// Context information for ActiveRecord objects.
{
public:
using Ptr = Poco::AutoPtr<Context>;
explicit Context(const Poco::Data::Session& session);
/// Creates the Context from an existing Poco::Data::Session.
Context(const std::string& connector, const std::string& connectionString);
/// Creates the Context from a connector name and connection string.
~Context() = default;
/// Destroys the Context.
Poco::Data::Session& session();
/// Returns the database session.
StatementPlaceholderProvider::Ptr statementPlaceholderProvider() const;
/// Returns a new StatementPlaceholderProvider.
private:
Context() = delete;
Context(const Context&) = delete;
Context& operator = (const Context&) = delete;
Poco::Data::Session _session;
};
//
// inlines
//
inline Poco::Data::Session& Context::session()
{
return _session;
}
} } // namespace Poco::ActiveRecord
#endif // ActiveRecord_Context_INCLUDED

View File

@ -0,0 +1,183 @@
//
// IDTraits.h
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: IDTraits
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecord_IDTraits_INCLUDED
#define ActiveRecord_IDTraits_INCLUDED
#include "Poco/Types.h"
#include "Poco/UUID.h"
#include "Poco/NumberFormatter.h"
namespace Poco {
namespace ActiveRecord {
template <typename T>
class IDTraits
/// Traits for ID/index types.
/// See specializations for details.
{
};
template <>
class IDTraits<Poco::UInt64>
{
public:
static const Poco::UInt64 INVALID_ID;
static bool isValid(Poco::UInt64 id)
{
return id != INVALID_ID;
}
static std::string toString(Poco::UInt64 id)
{
return Poco::NumberFormatter::format(id);
}
};
template <>
class IDTraits<Poco::Int64>
{
public:
static const Poco::Int64 INVALID_ID;
static bool isValid(Poco::Int64 id)
{
return id != INVALID_ID;
}
static std::string toString(Poco::Int64 id)
{
return Poco::NumberFormatter::format(id);
}
};
template <>
class IDTraits<Poco::UInt32>
{
public:
static const Poco::UInt32 INVALID_ID;
static bool isValid(Poco::UInt32 id)
{
return id != INVALID_ID;
}
static std::string toString(Poco::UInt32 id)
{
return Poco::NumberFormatter::format(id);
}
};
template <>
class IDTraits<Poco::Int32>
{
public:
static const Poco::Int32 INVALID_ID;
static bool isValid(Poco::Int32 id)
{
return id != INVALID_ID;
}
static std::string toString(Poco::Int32 id)
{
return Poco::NumberFormatter::format(id);
}
};
template <>
class IDTraits<Poco::UInt16>
{
public:
static const Poco::UInt16 INVALID_ID;
static bool isValid(Poco::UInt16 id)
{
return id != INVALID_ID;
}
static std::string toString(Poco::UInt16 id)
{
return Poco::NumberFormatter::format(id);
}
};
template <>
class IDTraits<Poco::Int16>
{
public:
static const Poco::Int16 INVALID_ID;
static bool isValid(Poco::Int16 id)
{
return id != INVALID_ID;
}
static std::string toString(Poco::Int16 id)
{
return Poco::NumberFormatter::format(id);
}
};
template <>
class IDTraits<std::string>
{
public:
static const std::string INVALID_ID;
static bool isValid(const std::string& id)
{
return !id.empty();
}
static std::string toString(const std::string& id)
{
return id;
}
};
template <>
class IDTraits<Poco::UUID>
{
public:
static const Poco::UUID INVALID_ID;
static bool isValid(const Poco::UUID& id)
{
return !id.isNull();
}
static std::string toString(const Poco::UUID& id)
{
return id.toString();
}
};
} } // namespace Poco::ActiveRecord
#endif // ActiveRecord_IDTraits_INCLUDED

View File

@ -0,0 +1,197 @@
//
// Query.h
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: Query
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecord_select_INCLUDED
#define ActiveRecord_select_INCLUDED
#include "Poco/ActiveRecord/ActiveRecord.h"
#include "Poco/ActiveRecord/Context.h"
#include "Poco/Data/Session.h"
#include "Poco/Data/Statement.h"
namespace Poco {
namespace ActiveRecord {
template <typename ActRec>
class Query
{
public:
explicit Query(Context::Ptr pContext):
_pContext(pContext),
_select(pContext->session())
{
select();
}
Query() = delete;
Query(const Query&) = delete;
~Query() = default;
Query& operator = (const Query&) = delete;
Query& where(const std::string& clause)
{
_select << " WHERE " << fixPlaceholders(clause);
return *this;
}
Query& orderBy(const std::string& order)
{
_select << " ORDER BY " << order;
return *this;
}
template <typename T>
Query& bind(const T& value)
{
_select, Poco::Data::Keywords::bind(value);
return *this;
}
Query& offset(std::size_t offset)
{
_offset = offset;
return *this;
}
Query& limit(std::size_t limit)
{
_limit = limit;
return *this;
}
Query& filter(const std::function<bool(const ActRec&)>& fn)
{
_filter = fn;
return *this;
}
Query& filter(std::function<bool(const ActRec&)>&& fn)
{
_filter = std::move(fn);
return *this;
}
std::vector<typename ActRec::Ptr> execute()
{
std::vector<typename ActRec::Ptr> result;
typename ActRec::Ptr pObject = new ActRec;
ActRec::queryInto(_select, *pObject);
_select, Poco::Data::Keywords::limit(1);
std::size_t index = 0;
while (!_select.done())
{
if (_select.execute())
{
if (!_filter || _filter(*pObject))
{
if (index >= _offset && (_limit == 0 || result.size() < _limit))
{
typename ActRec::Ptr pClone = new ActRec(*pObject);
result.push_back(ActiveRecord<ActRec>::withContext(pClone, _pContext));
}
index++;
}
}
}
_totalResults = index;
return result;
}
std::size_t totalResults() const
{
return _totalResults;
}
void reset()
{
_offset = 0;
_limit = 0;
_totalResults = 0;
std::function<bool(const ActRec&)> emptyFilter;
_filter.swap(emptyFilter);
_select = Poco::Data::Statement(_pContext->session());
select();
}
protected:
void select()
{
_select << "SELECT ";
const auto& columns = ActRec::columns();
bool needComma = false;
for (const auto& c: columns)
{
if (needComma) _select << ", ";
_select << c;
needComma = true;
}
_select << " FROM " << ActRec::table();
}
std::string fixPlaceholders(const std::string& clause)
{
auto pSPP = _pContext->statementPlaceholderProvider();
std::string result;
auto it = clause.begin();
auto end = clause.end();
while (it != end)
{
switch (*it)
{
case '"':
result += *it++;
while (it != end && *it != '"') result += *it++;
if (it != end) result += *it++;
break;
case '\'':
result += *it++;
while (it != end && *it != '\'') result += *it++;
if (it != end) result += *it++;
break;
case '?':
result += pSPP->next();
++it;
break;
default:
result += *it++;
break;
}
}
return result;
}
private:
Context::Ptr _pContext;
Poco::Data::Statement _select;
std::function<bool(const ActRec&)> _filter;
std::size_t _offset = 0;
std::size_t _limit = 0;
std::size_t _totalResults = 0;
};
} } // namespace Poco::ActiveRecord
#endif // ActiveRecord_select_INCLUDED

View File

@ -0,0 +1,62 @@
//
// StatementPlaceholderProvider.h
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: StatementPlaceholderProvider
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecord_StatementPlaceholderProvider_INCLUDED
#define ActiveRecord_StatementPlaceholderProvider_INCLUDED
#include "Poco/ActiveRecord/ActiveRecordLib.h"
#include <string>
#include <memory>
namespace Poco {
namespace ActiveRecord {
class ActiveRecordLib_API StatementPlaceholderProvider
{
public:
using Ptr = std::unique_ptr<StatementPlaceholderProvider>;
virtual void reset() = 0;
virtual std::string next() = 0;
virtual ~StatementPlaceholderProvider();
};
class ActiveRecordLib_API DefaultStatementPlaceholderProvider: public StatementPlaceholderProvider
{
public:
void reset();
std::string next();
};
class ActiveRecordLib_API PostgresStatementPlaceholderProvider: public StatementPlaceholderProvider
{
public:
void reset();
std::string next();
private:
int _n = 1;
};
} } // namespace Poco::ActiveRecord
#endif // ActiveRecord_StatementPlaceholderProvider_INCLUDED

View File

@ -0,0 +1,70 @@
//
// TypeHandler.h
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: TypeHandler
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecord_TypeHandler_INCLUDED
#define ActiveRecord_TypeHandler_INCLUDED
#include "Poco/Data/TypeHandler.h"
#include "Poco/ThreadLocal.h"
#include "Poco/UUID.h"
#include <map>
namespace Poco {
namespace Data {
template <>
class TypeHandler<Poco::UUID>
{
public:
using UUIDMap = std::map<std::size_t, std::string>;
static std::size_t size()
{
return 1;
}
static void bind(std::size_t pos, const Poco::UUID& uuid, AbstractBinder::Ptr pBinder, AbstractBinder::Direction dir)
{
static Poco::ThreadLocal<UUIDMap> uuidMap;
std::string& uuidString = (*uuidMap)[pos];
uuidString = uuid.toString();
TypeHandler<std::string>::bind(pos++, uuidString, pBinder, dir);
}
static void extract(std::size_t pos, Poco::UUID& uuid, const Poco::UUID& deflt, AbstractExtractor::Ptr pExtr)
{
std::string defltString = deflt.toString();
std::string uuidString;
TypeHandler<std::string>::extract(pos++, uuidString, defltString, pExtr);
uuid.parse(uuidString);
}
static void prepare(std::size_t pos, const Poco::UUID& uuid, AbstractPreparator::Ptr pPrep)
{
static Poco::ThreadLocal<UUIDMap> uuidMap;
std::string& uuidString = (*uuidMap)[pos];
uuidString = uuid.toString();
TypeHandler<std::string>::prepare(pos++, uuidString, pPrep);
}
};
} } // namespace Poco::Data
#endif // ActiveRecord_TypeHandler_INCLUDED

View File

@ -0,0 +1,62 @@
//
// ActiveRecord.h
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: ActiveRecord
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/ActiveRecord/ActiveRecord.h"
#include "Poco/NumberFormatter.h"
#include "Poco/Exception.h"
using namespace Poco::Data::Keywords;
using namespace std::string_literals;
namespace Poco {
namespace ActiveRecord {
void ActiveRecordBase::attach(Context::Ptr pContext)
{
if (_pContext) throw Poco::IllegalStateException("ActiveRecord already has a Context");
if (!pContext) throw Poco::InvalidArgumentException("Cannot attach to a null Context");
_pContext = pContext;
}
void ActiveRecordBase::detach()
{
_pContext.reset();
}
void ActiveRecordBase::create(Context::Ptr pContext)
{
attach(pContext);
insert();
}
bool ActiveRecordBase::isValid() const
{
return true;
}
std::string KeylessActiveRecord::toString() const
{
return ""s;
}
} } // namespace Poco::ActiveRecord

View File

@ -0,0 +1,43 @@
//
// Context.cpp
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: Context
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/ActiveRecord/Context.h"
namespace Poco {
namespace ActiveRecord {
Context::Context(const Poco::Data::Session& session):
_session(session)
{
}
Context::Context(const std::string& connector, const std::string& connectionString):
_session(connector, connectionString)
{
}
StatementPlaceholderProvider::Ptr Context::statementPlaceholderProvider() const
{
if (_session.connector() == "postgresql")
return std::make_unique<PostgresStatementPlaceholderProvider>();
else
return std::make_unique<DefaultStatementPlaceholderProvider>();
}
} } // namespace Poco::ActiveRecord

View File

@ -0,0 +1,32 @@
//
// IDTraits.cpp
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: IDTraits
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/ActiveRecord/IDTraits.h"
namespace Poco {
namespace ActiveRecord {
const Poco::UInt64 IDTraits<Poco::UInt64>::INVALID_ID = std::numeric_limits<Poco::UInt64>::max();
const Poco::Int64 IDTraits<Poco::Int64>::INVALID_ID = std::numeric_limits<Poco::Int64>::max();
const Poco::UInt32 IDTraits<Poco::UInt32>::INVALID_ID = std::numeric_limits<Poco::UInt32>::max();
const Poco::Int32 IDTraits<Poco::Int32>::INVALID_ID = std::numeric_limits<Poco::Int32>::max();
const Poco::UInt16 IDTraits<Poco::UInt16>::INVALID_ID = std::numeric_limits<Poco::UInt16>::max();
const Poco::Int16 IDTraits<Poco::Int16>::INVALID_ID = std::numeric_limits<Poco::Int16>::max();
const std::string IDTraits<std::string>::INVALID_ID;
const Poco::UUID IDTraits<Poco::UUID>::INVALID_ID;
} } // namespace Poco::ActiveRecord

View File

@ -0,0 +1,54 @@
//
// StatementPlaceholderProvider.cpp
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: StatementPlaceholderProvider
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/ActiveRecord/StatementPlaceholderProvider.h"
#include "Poco/Format.h"
using namespace std::string_literals;
namespace Poco {
namespace ActiveRecord {
StatementPlaceholderProvider::~StatementPlaceholderProvider()
{
}
void DefaultStatementPlaceholderProvider::reset()
{
}
std::string DefaultStatementPlaceholderProvider::next()
{
return "?"s;
}
void PostgresStatementPlaceholderProvider::reset()
{
_n = 1;
}
std::string PostgresStatementPlaceholderProvider::next()
{
return Poco::format("$%d"s, _n++);
}
} } // namespace Poco::ActiveRecord

View File

@ -0,0 +1,36 @@
# Sources
file(GLOB SRCS_G "src/*.cpp")
POCO_SOURCES_AUTO(TEST_SRCS ${SRCS_G})
# Headers
file(GLOB_RECURSE HDRS_G "src/*.h")
POCO_HEADERS_AUTO(TEST_SRCS ${HDRS_G})
POCO_SOURCES_AUTO_PLAT(TEST_SRCS OFF
src/WinDriver.cpp
)
POCO_SOURCES_AUTO_PLAT(TEST_SRCS WINCE
src/WinCEDriver.cpp
)
add_executable(ActiveRecord-testrunner ${TEST_SRCS})
if(ANDROID)
add_test(
NAME ActiveRecord
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -DANDROID_NDK=${ANDROID_NDK} -DLIBRARY_DIR=${CMAKE_BINARY_DIR}/lib -DUNITTEST=${CMAKE_BINARY_DIR}/bin/ActiveRecord-testrunner -DTEST_PARAMETER=-all -P ${CMAKE_SOURCE_DIR}/cmake/ExecuteOnAndroid.cmake
)
else()
add_test(
NAME ActiveRecord
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND ActiveRecord-testrunner -ignore ${CMAKE_SOURCE_DIR}/cppignore.lnx -all
)
set_tests_properties(ActiveRecord PROPERTIES ENVIRONMENT POCO_BASE=${CMAKE_SOURCE_DIR})
endif()
target_link_libraries(ActiveRecord-testrunner PUBLIC Poco::DataSQLite Poco::Data Poco::ActiveRecord CppUnit)
target_include_directories(ActiveRecord-testrunner
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
)

View File

@ -0,0 +1,16 @@
#
# Makefile
#
# Makefile for Poco ActiveRecord testsuite
#
include $(POCO_BASE)/build/rules/global
objects = ActiveRecordTestSuite ActiveRecordTest Driver \
Employee Role
target = testrunner
target_version = 1
target_libs = PocoActiveRecord PocoDataSQLite PocoData PocoFoundation CppUnit
include $(POCO_BASE)/build/rules/exec

View File

@ -0,0 +1,15 @@
<project namespace="ORM">
<class name="Employee" table="employees">
<property name="id" type="uuid"/>
<property name="name" type="string"/>
<property name="ssn" type="string"/>
<property name="role" type="int16" references="Role"/>
<property name="manager" type="uuid" references="Employee" cardinality="?"/>
</class>
<class name="Role" table="roles" autoIncrementID="true">
<property name="id" type="int16"/>
<property name="name" type="string"/>
<property name="description" type="string"/>
</class>
</project>

View File

@ -0,0 +1,161 @@
//
// Employee.h
//
// This file has been generated from ORM.xml. Do not edit.
//
#ifndef ORM_Employee_INCLUDED
#define ORM_Employee_INCLUDED
#include "Poco/ActiveRecord/ActiveRecord.h"
#include "ORM/Role.h"
namespace ORM {
class Employee: public Poco::ActiveRecord::ActiveRecord<Poco::UUID>
{
public:
using Ptr = Poco::AutoPtr<Employee>;
explicit Employee(ID id);
Employee() = default;
Employee(const Employee& other);
~Employee() = default;
const std::string& name() const;
Employee& name(const std::string& value);
const std::string& ssn() const;
Employee& ssn(const std::string& value);
Role::Ptr role() const;
Poco::Int16 roleID() const;
Employee& role(Role::Ptr pObject);
Employee& roleID(Poco::Int16 id);
Employee::Ptr manager() const;
const Poco::UUID& managerID() const;
Employee& manager(Employee::Ptr pObject);
Employee& managerID(const Poco::UUID& id);
static Ptr find(Poco::ActiveRecord::Context::Ptr pContext, const ID& id);
void insert();
void update();
void remove();
static const std::vector<std::string>& columns();
static const std::string& table();
private:
std::string _name;
std::string _ssn;
Poco::Int16 _role = Role::INVALID_ID;
Poco::UUID _manager;
friend class Poco::Data::TypeHandler<Employee>;
};
inline const std::string& Employee::name() const
{
return _name;
}
inline Employee& Employee::name(const std::string& value)
{
_name = value;
return *this;
}
inline const std::string& Employee::ssn() const
{
return _ssn;
}
inline Employee& Employee::ssn(const std::string& value)
{
_ssn = value;
return *this;
}
inline Poco::Int16 Employee::roleID() const
{
return _role;
}
inline Employee& Employee::roleID(Poco::Int16 value)
{
_role = value;
return *this;
}
inline const Poco::UUID& Employee::managerID() const
{
return _manager;
}
inline Employee& Employee::managerID(const Poco::UUID& value)
{
_manager = value;
return *this;
}
} // namespace ORM
namespace Poco {
namespace Data {
template <>
class TypeHandler<ORM::Employee>
{
public:
static std::size_t size()
{
return 4;
}
static void bind(std::size_t pos, const ORM::Employee& ar, AbstractBinder::Ptr pBinder, AbstractBinder::Direction dir)
{
TypeHandler<std::string>::bind(pos++, ar._name, pBinder, dir);
TypeHandler<std::string>::bind(pos++, ar._ssn, pBinder, dir);
TypeHandler<Poco::Int16>::bind(pos++, ar._role, pBinder, dir);
TypeHandler<Poco::UUID>::bind(pos++, ar._manager, pBinder, dir);
}
static void extract(std::size_t pos, ORM::Employee& ar, const ORM::Employee& deflt, AbstractExtractor::Ptr pExtr)
{
TypeHandler<std::string>::extract(pos++, ar._name, deflt._name, pExtr);
TypeHandler<std::string>::extract(pos++, ar._ssn, deflt._ssn, pExtr);
TypeHandler<Poco::Int16>::extract(pos++, ar._role, deflt._role, pExtr);
TypeHandler<Poco::UUID>::extract(pos++, ar._manager, deflt._manager, pExtr);
}
static void prepare(std::size_t pos, const ORM::Employee& ar, AbstractPreparator::Ptr pPrep)
{
TypeHandler<std::string>::prepare(pos++, ar._name, pPrep);
TypeHandler<std::string>::prepare(pos++, ar._ssn, pPrep);
TypeHandler<Poco::Int16>::prepare(pos++, ar._role, pPrep);
TypeHandler<Poco::UUID>::prepare(pos++, ar._manager, pPrep);
}
};
} } // namespace Poco::Data
#endif // ORM_Employee_INCLUDED

View File

@ -0,0 +1,116 @@
//
// Role.h
//
// This file has been generated from ORM.xml. Do not edit.
//
#ifndef ORM_Role_INCLUDED
#define ORM_Role_INCLUDED
#include "Poco/ActiveRecord/ActiveRecord.h"
namespace ORM {
class Role: public Poco::ActiveRecord::ActiveRecord<Poco::Int16>
{
public:
using Ptr = Poco::AutoPtr<Role>;
explicit Role(ID id);
Role() = default;
Role(const Role& other);
~Role() = default;
const std::string& name() const;
Role& name(const std::string& value);
const std::string& description() const;
Role& description(const std::string& value);
static Ptr find(Poco::ActiveRecord::Context::Ptr pContext, const ID& id);
void insert();
void update();
void remove();
static const std::vector<std::string>& columns();
static const std::string& table();
private:
std::string _name;
std::string _description;
friend class Poco::Data::TypeHandler<Role>;
};
inline const std::string& Role::name() const
{
return _name;
}
inline Role& Role::name(const std::string& value)
{
_name = value;
return *this;
}
inline const std::string& Role::description() const
{
return _description;
}
inline Role& Role::description(const std::string& value)
{
_description = value;
return *this;
}
} // namespace ORM
namespace Poco {
namespace Data {
template <>
class TypeHandler<ORM::Role>
{
public:
static std::size_t size()
{
return 2;
}
static void bind(std::size_t pos, const ORM::Role& ar, AbstractBinder::Ptr pBinder, AbstractBinder::Direction dir)
{
TypeHandler<std::string>::bind(pos++, ar._name, pBinder, dir);
TypeHandler<std::string>::bind(pos++, ar._description, pBinder, dir);
}
static void extract(std::size_t pos, ORM::Role& ar, const ORM::Role& deflt, AbstractExtractor::Ptr pExtr)
{
TypeHandler<std::string>::extract(pos++, ar._name, deflt._name, pExtr);
TypeHandler<std::string>::extract(pos++, ar._description, deflt._description, pExtr);
}
static void prepare(std::size_t pos, const ORM::Role& ar, AbstractPreparator::Ptr pPrep)
{
TypeHandler<std::string>::prepare(pos++, ar._name, pPrep);
TypeHandler<std::string>::prepare(pos++, ar._description, pPrep);
}
};
} } // namespace Poco::Data
#endif // ORM_Role_INCLUDED

View File

@ -0,0 +1,333 @@
//
// ActiveRecordTest.cpp
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "ActiveRecordTest.h"
#include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h"
#include "Poco/ActiveRecord/Context.h"
#include "Poco/ActiveRecord/Query.h"
#include "Poco/Data/SQLite/Connector.h"
#include "Poco/Data/Statement.h"
#include "Poco/UUIDGenerator.h"
#include "ORM/Employee.h"
#include "ORM/Role.h"
using namespace std::string_literals;
using namespace Poco::Data::Keywords;
using Poco::ActiveRecord::Context;
using Poco::ActiveRecord::Query;
using ORM::Employee;
using ORM::Role;
const std::string ActiveRecordTest::CONNECTOR("SQLite");
const std::string ActiveRecordTest::CONNECTION_STRING("ORM.sqlite");
ActiveRecordTest::ActiveRecordTest(const std::string& name): CppUnit::TestCase(name)
{
}
ActiveRecordTest::~ActiveRecordTest()
{
}
void ActiveRecordTest::testInsert()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
Role::Ptr pDeveloper = new Role;
pDeveloper->name("Developer").description("Developer role");
assertTrue (!pDeveloper->isValid());
pDeveloper->create(pContext);
assertTrue (pDeveloper->isValid());
int n = 0;
session << "SELECT COUNT(*) FROM roles", into(n), now;
assertTrue (n == 1);
assertTrue (pDeveloper->id() == 1);
Role::Ptr pSeniorDeveloper = new Role;
pSeniorDeveloper->name("Senior Developer").description("Senior developer role");
pSeniorDeveloper->create(pContext);
session << "SELECT COUNT(*) FROM roles", into(n), now;
assertTrue (n == 2);
assertTrue (pSeniorDeveloper->id() == 2);
}
void ActiveRecordTest::testFind()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Role::Ptr pRole = Role::find(pContext, 1);
assertTrue (!pRole.isNull());
assertTrue (pRole->name() == "Developer");
assertTrue (pRole->description() == "Developer role");
pRole = Role::find(pContext, 2);
assertTrue (!pRole.isNull());
assertTrue (pRole->name() == "Senior Developer");
assertTrue (pRole->description() == "Senior developer role");
pRole = Role::find(pContext, 3);
assertTrue (!pRole.isNull());
assertTrue (pRole->name() == "Manager");
assertTrue (pRole->description() == "Manager role");
pRole = Role::find(pContext, 4);
assertTrue (pRole.isNull());
}
void ActiveRecordTest::testUpdate()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Role::Ptr pRole = Role::find(pContext, 1);
assertTrue (!pRole.isNull());
pRole->name("Junior Developer").description("Junior developer role");
pRole->update();
pRole = Role::find(pContext, 1);
assertTrue (!pRole.isNull());
assertTrue (pRole->name() == "Junior Developer");
assertTrue (pRole->description() == "Junior developer role");
}
void ActiveRecordTest::testDelete()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Role::Ptr pRole = Role::find(pContext, 1);
assertTrue (!pRole.isNull());
pRole->remove();
pRole = Role::find(pContext, 1);
assertTrue (pRole.isNull());
}
void ActiveRecordTest::testRelations()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Employee::Ptr pManager = new Employee(Poco::UUIDGenerator().createOne());
pManager->name("Bill Lumbergh").ssn("23452343").roleID(3);
pManager->create(pContext);
Role::Ptr pManagerRole = pManager->role();
assertFalse (pManagerRole.isNull());
assertTrue (pManagerRole->id() == 3);
Employee::Ptr pEmployee = new Employee(Poco::UUIDGenerator().createOne());
pEmployee->name("Michael Bolton").ssn("123987123").roleID(2).manager(pManager);
pEmployee->create(pContext);
assertTrue (pEmployee->managerID() == pManager->id());
}
void ActiveRecordTest::testQuery()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Query<Role> query(pContext);
auto result = query.execute();
assertTrue (result.size() == 3);
}
void ActiveRecordTest::testQueryWhere()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Query<Role> query(pContext);
query.where("name = 'Senior Developer'");
auto result = query.execute();
assertTrue (result.size() == 1);
assertTrue (result[0]->name() == "Senior Developer");
}
void ActiveRecordTest::testQueryWhereBind()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Query<Role> query(pContext);
query.where("name = ?").bind("Senior Developer"s);
auto result = query.execute();
assertTrue (result.size() == 1);
assertTrue (result[0]->name() == "Senior Developer");
}
void ActiveRecordTest::testQueryFilter()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Query<Role> query(pContext);
query.filter(
[](const Role& role)
{
return role.name() == "Senior Developer";
}
);
auto result = query.execute();
assertTrue (result.size() == 1);
assertTrue (result[0]->name() == "Senior Developer");
}
void ActiveRecordTest::testQueryOrderBy()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Query<Role> query(pContext);
query.orderBy("id DESC");
auto result = query.execute();
assertTrue (result.size() == 3);
assertTrue (result[0]->name() == "Manager");
}
void ActiveRecordTest::testQueryPaging()
{
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
Context::Ptr pContext = new Context(session);
createRoles(pContext);
Query<Role> query(pContext);
auto result = query.orderBy("id").offset(0).limit(2).execute();
assertTrue (result.size() == 2);
assertTrue (result[0]->name() == "Developer");
assertTrue (result[1]->name() == "Senior Developer");
query.reset();
result = query.orderBy("id").offset(1).limit(2).execute();
assertTrue (result.size() == 2);
assertTrue (result[0]->name() == "Senior Developer");
assertTrue (result[1]->name() == "Manager");
}
void ActiveRecordTest::setUp()
{
Poco::Data::SQLite::Connector::registerConnector();
Poco::Data::Session session(CONNECTOR, CONNECTION_STRING);
session << "DROP TABLE IF EXISTS employees", now;
session << "DROP TABLE IF EXISTS roles", now;
session
<< "CREATE TABLE employees ("
<< " id CHAR(36),"
<< " name VARCHAR(64),"
<< " ssn VARCHAR(32),"
<< " role INTEGER,"
<< " manager CHAR(36)"
<< ")",
now;
session
<< "CREATE TABLE roles ("
<< " id INTEGER PRIMARY KEY AUTOINCREMENT,"
<< " name VARCHAR(64),"
<< " description VARCHAR(256)"
<< ")",
now;
}
void ActiveRecordTest::tearDown()
{
Poco::Data::SQLite::Connector::unregisterConnector();
}
void ActiveRecordTest::createRoles(Poco::ActiveRecord::Context::Ptr pContext)
{
Role::Ptr pDeveloper = new Role;
pDeveloper->name("Developer").description("Developer role");
pDeveloper->create(pContext);
Role::Ptr pSeniorDeveloper = new Role;
pSeniorDeveloper->name("Senior Developer").description("Senior developer role");
pSeniorDeveloper->create(pContext);
Role::Ptr pManager = new Role;
pManager->name("Manager").description("Manager role");
pManager->create(pContext);
}
CppUnit::Test* ActiveRecordTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ActiveRecordTest");
CppUnit_addTest(pSuite, ActiveRecordTest, testInsert);
CppUnit_addTest(pSuite, ActiveRecordTest, testFind);
CppUnit_addTest(pSuite, ActiveRecordTest, testUpdate);
CppUnit_addTest(pSuite, ActiveRecordTest, testDelete);
CppUnit_addTest(pSuite, ActiveRecordTest, testRelations);
CppUnit_addTest(pSuite, ActiveRecordTest, testQuery);
CppUnit_addTest(pSuite, ActiveRecordTest, testQueryWhere);
CppUnit_addTest(pSuite, ActiveRecordTest, testQueryWhereBind);
CppUnit_addTest(pSuite, ActiveRecordTest, testQueryOrderBy);
CppUnit_addTest(pSuite, ActiveRecordTest, testQueryFilter);
CppUnit_addTest(pSuite, ActiveRecordTest, testQueryPaging);
return pSuite;
}

View File

@ -0,0 +1,51 @@
//
// ActiveRecordTest.h
//
// Definition of the ActiveRecordTest class.
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecordTest_INCLUDED
#define ActiveRecordTest_INCLUDED
#include "Poco/ActiveRecord/Context.h"
#include "CppUnit/TestCase.h"
class ActiveRecordTest: public CppUnit::TestCase
{
public:
ActiveRecordTest(const std::string& name);
~ActiveRecordTest();
void testInsert();
void testFind();
void testUpdate();
void testDelete();
void testRelations();
void testQuery();
void testQueryWhere();
void testQueryWhereBind();
void testQueryOrderBy();
void testQueryFilter();
void testQueryPaging();
void setUp();
void tearDown();
void createRoles(Poco::ActiveRecord::Context::Ptr pContext);
static CppUnit::Test* suite();
static const std::string CONNECTOR;
static const std::string CONNECTION_STRING;
};
#endif // ActiveRecordTest_INCLUDED

View File

@ -0,0 +1,22 @@
//
// ActiveRecordTestSuite.cpp
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "ActiveRecordTestSuite.h"
#include "ActiveRecordTest.h"
CppUnit::Test* ActiveRecordTestSuite::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ActiveRecordTestSuite");
pSuite->addTest(ActiveRecordTest::suite());
return pSuite;
}

View File

@ -0,0 +1,27 @@
//
// ActiveRecordTestSuite.h
//
// Definition of the ActiveRecordTestSuite class.
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecordTestSuite_INCLUDED
#define ActiveRecordTestSuite_INCLUDED
#include "CppUnit/TestSuite.h"
class ActiveRecordTestSuite
{
public:
static CppUnit::Test* suite();
};
#endif // ActiveRecordTestSuite_INCLUDED

View File

@ -0,0 +1,17 @@
//
// Driver.cpp
//
// Console-based test driver for Poco ActiveRecord.
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "CppUnit/TestRunner.h"
#include "ActiveRecordTestSuite.h"
CppUnitMain(ActiveRecordTestSuite)

View File

@ -0,0 +1,151 @@
//
// Employee.cpp
//
// This file has been generated from ORM.xml. Do not edit.
//
#include "ORM/Employee.h"
#include "Poco/UUIDGenerator.h"
using namespace std::string_literals;
using namespace Poco::Data::Keywords;
namespace ORM {
Employee::Employee(ID id):
Poco::ActiveRecord::ActiveRecord<Poco::UUID>(id)
{
}
Employee::Employee(const Employee& other):
Poco::ActiveRecord::ActiveRecord<Poco::UUID>(other),
_name(other._name),
_ssn(other._ssn),
_role(other._role),
_manager(other._manager)
{
}
Role::Ptr Employee::role() const
{
return Role::find(context(), _role);
}
Employee& Employee::role(Role::Ptr pObject)
{
if (pObject)
_role = pObject->id();
else
_role = Role::INVALID_ID;
return *this;
}
Employee::Ptr Employee::manager() const
{
return Employee::find(context(), _manager);
}
Employee& Employee::manager(Employee::Ptr pObject)
{
if (pObject)
_manager = pObject->id();
else
_manager = Employee::INVALID_ID;
return *this;
}
Employee::Ptr Employee::find(Poco::ActiveRecord::Context::Ptr pContext, const ID& id)
{
Poco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(pContext->statementPlaceholderProvider());
Employee::Ptr pObject(new Employee);
pContext->session()
<< "SELECT id, name, ssn, role, manager"
<< " FROM employees"
<< " WHERE id = " << pSPP->next(),
into(pObject->mutableID()),
into(*pObject),
bind(id),
now;
return withContext(pObject, pContext);
}
void Employee::insert()
{
Poco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider());
if (id().isNull())
{
mutableID() = Poco::UUIDGenerator().createRandom();
}
context()->session()
<< "INSERT INTO employees (id, name, ssn, role, manager)"
<< " VALUES (" << pSPP->next() << ", " << pSPP->next() << ", " << pSPP->next() << ", " << pSPP->next() << ", " << pSPP->next() << ")",
bind(id()),
use(*this),
now;
}
void Employee::update()
{
Poco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider());
context()->session()
<< "UPDATE employees"
<< " SET name = " << pSPP->next() << ", ssn = " << pSPP->next() << ", role = " << pSPP->next() << ", manager = " << pSPP->next()
<< " WHERE id = " << pSPP->next(),
use(*this),
bind(id()),
now;
}
void Employee::remove()
{
Poco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider());
context()->session()
<< "DELETE FROM employees"
<< " WHERE id = " << pSPP->next(),
bind(id()),
now;
}
const std::vector<std::string>& Employee::columns()
{
static const std::vector<std::string> cols =
{
"id"s,
"name"s,
"ssn"s,
"role"s,
"manager"s,
};
return cols;
}
const std::string& Employee::table()
{
static const std::string t = "employees";
return t;
}
} // namespace ORM

View File

@ -0,0 +1,109 @@
//
// Role.cpp
//
// This file has been generated from ORM.xml. Do not edit.
//
#include "ORM/Role.h"
using namespace std::string_literals;
using namespace Poco::Data::Keywords;
namespace ORM {
Role::Role(ID id):
Poco::ActiveRecord::ActiveRecord<Poco::Int16>(id)
{
}
Role::Role(const Role& other):
Poco::ActiveRecord::ActiveRecord<Poco::Int16>(other),
_name(other._name),
_description(other._description)
{
}
Role::Ptr Role::find(Poco::ActiveRecord::Context::Ptr pContext, const ID& id)
{
Poco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(pContext->statementPlaceholderProvider());
Role::Ptr pObject(new Role);
pContext->session()
<< "SELECT id, name, description"
<< " FROM roles"
<< " WHERE id = " << pSPP->next(),
into(pObject->mutableID()),
into(*pObject),
bind(id),
now;
return withContext(pObject, pContext);
}
void Role::insert()
{
Poco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider());
context()->session()
<< "INSERT INTO roles (id, name, description)"
<< " VALUES (NULL, " << pSPP->next() << ", " << pSPP->next() << ")",
use(*this),
now;
updateID(context()->session());
}
void Role::update()
{
Poco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider());
context()->session()
<< "UPDATE roles"
<< " SET name = " << pSPP->next() << ", description = " << pSPP->next()
<< " WHERE id = " << pSPP->next(),
use(*this),
bind(id()),
now;
}
void Role::remove()
{
Poco::ActiveRecord::StatementPlaceholderProvider::Ptr pSPP(context()->statementPlaceholderProvider());
context()->session()
<< "DELETE FROM roles"
<< " WHERE id = " << pSPP->next(),
bind(id()),
now;
}
const std::vector<std::string>& Role::columns()
{
static const std::vector<std::string> cols =
{
"id"s,
"name"s,
"description"s,
};
return cols;
}
const std::string& Role::table()
{
static const std::string t = "roles";
return t;
}
} // namespace ORM

View File

@ -0,0 +1,30 @@
//
// WinCEDriver.cpp
//
// Console-based test driver for Windows CE.
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "CppUnit/TestRunner.h"
#include "ActiveRecordTestSuite.h"
#include <cstdlib>
int wmain(int argc, wchar_t* argv[])
{
std::vector<std::string> args;
for (int i = 0; i < argc; ++i)
{
char buffer[1024];
std::wcstombs(buffer, argv[i], sizeof(buffer));
args.push_back(std::string(buffer));
}
CppUnit::TestRunner runner;
runner.addTest("ActiveRecordTestSuite", ActiveRecordTestSuite::suite());
return runner.run(args) ? 0 : 1;
}

View File

@ -0,0 +1,28 @@
//
// WinDriver.cpp
//
// Windows test driver for Poco ActiveRecord.
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: Apache-2.0
//
#include "WinTestRunner/WinTestRunner.h"
#include "ActiveRecordTestSuite.h"
class TestDriver: public CppUnit::WinTestRunnerApp
{
void TestMain()
{
CppUnit::WinTestRunner runner;
runner.addTest(ActiveRecordTestSuite::suite());
runner.run();
}
};
TestDriver theDriver;

View File

@ -175,6 +175,14 @@ option(ENABLE_POCODOC "Enable Poco Documentation Generator" OFF)
option(ENABLE_PAGECOMPILER "Enable PageCompiler" ON)
option(ENABLE_PAGECOMPILER_FILE2PAGE "Enable File2Page" ON)
if(ENABLE_DATA)
option(ENABLE_ACTIVERECORD "Enable ActiveRecord" ON)
option(ENABLE_ACTIVERECORD_COMPILER "Enable ActiveRecord Compiler" ON)
else()
option(ENABLE_ACTIVERECORD "Enable ActiveRecord" OFF)
option(ENABLE_ACTIVERECORD_COMPILER "Enable ActiveRecord Compiler" OFF)
endif()
option(ENABLE_TESTS
"Set to OFF|ON (default is OFF) to control build of POCO tests & samples" OFF)
@ -327,6 +335,16 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/Redis AND ENABLE_REDIS)
list(APPEND Poco_COMPONENTS "Redis")
endif()
if(EXISTS ${PROJECT_SOURCE_DIR}/ActiveRecord AND ENABLE_ACTIVERECORD)
add_subdirectory(ActiveRecord)
list(APPEND Poco_COMPONENTS "ActiveRecord")
endif()
if(EXISTS ${PROJECT_SOURCE_DIR}/ActiveRecord/Compiler AND ENABLE_ACTIVERECORD_COMPILER)
add_subdirectory(ActiveRecord/Compiler)
list(APPEND Poco_COMPONENTS "ActiveRecordCompiler")
endif()
if(EXISTS ${PROJECT_SOURCE_DIR}/PDF AND ENABLE_PDF)
add_subdirectory(PDF)
list(APPEND Poco_COMPONENTS "PDF")

View File

@ -78,7 +78,7 @@ poco: libexecs $(if $(TESTS),tests) $(if $(SAMPLES),samples)
all: libexecs tests samples
INSTALLDIR = $(DESTDIR)$(POCO_PREFIX)
COMPONENTS = Foundation Encodings XML JSON Util Net Crypto NetSSL_OpenSSL Data Data/SQLite Data/ODBC Data/MySQL Zip PageCompiler PageCompiler/File2Page JWT CppParser PDF MongoDB Redis
COMPONENTS = Foundation Encodings XML JSON Util Net Crypto NetSSL_OpenSSL Data Data/SQLite Data/ODBC Data/MySQL Zip PageCompiler PageCompiler/File2Page JWT CppParser PDF MongoDB Redis ActiveRecord ActiveRecord/Compiler
cppunit:
$(MAKE) -C $(POCO_BASE)/CppUnit
@ -115,10 +115,10 @@ endif
find $(INSTALLDIR)/lib -name "libPoco*" -type f -exec rm -f {} \;
find $(INSTALLDIR)/lib -name "libPoco*" -type l -exec rm -f {} \;
libexecs = Foundation-libexec Encodings-libexec XML-libexec JSON-libexec Util-libexec Net-libexec Crypto-libexec NetSSL_OpenSSL-libexec Data-libexec Data/SQLite-libexec Data/ODBC-libexec Data/MySQL-libexec Zip-libexec JWT-libexec PageCompiler-libexec PageCompiler/File2Page-libexec CppParser-libexec PDF-libexec MongoDB-libexec Redis-libexec
tests = Foundation-tests Encodings-tests XML-tests JSON-tests Util-tests Net-tests Crypto-tests NetSSL_OpenSSL-tests Data-tests Data/SQLite-tests Data/ODBC-tests Data/MySQL-tests JWT-tests Zip-tests CppParser-tests PDF-tests MongoDB-tests Redis-tests
libexecs = Foundation-libexec Encodings-libexec XML-libexec JSON-libexec Util-libexec Net-libexec Crypto-libexec NetSSL_OpenSSL-libexec Data-libexec Data/SQLite-libexec Data/ODBC-libexec Data/MySQL-libexec Zip-libexec JWT-libexec PageCompiler-libexec PageCompiler/File2Page-libexec CppParser-libexec PDF-libexec MongoDB-libexec Redis-libexec ActiveRecord-libexec ActiveRecord/Compiler-libexec
tests = Foundation-tests Encodings-tests XML-tests JSON-tests Util-tests Net-tests Crypto-tests NetSSL_OpenSSL-tests Data-tests Data/SQLite-tests Data/ODBC-tests Data/MySQL-tests JWT-tests Zip-tests CppParser-tests PDF-tests MongoDB-tests Redis-tests ActiveRecord-tests
samples = Foundation-samples Encodings-samples XML-samples JSON-samples Util-samples Net-samples Crypto-samples NetSSL_OpenSSL-samples Data-samples MongoDB-samples Zip-samples PageCompiler-samples PDF-samples
cleans = Foundation-clean Encodings-clean XML-clean JSON-clean Util-clean Net-clean Crypto-clean NetSSL_OpenSSL-clean Data-clean Data/SQLite-clean Data/ODBC-clean Data/MySQL-clean JWT-clean Zip-clean PageCompiler-clean PageCompiler/File2Page-clean CppParser-clean PDF-clean MongoDB-clean Redis-clean
cleans = Foundation-clean Encodings-clean XML-clean JSON-clean Util-clean Net-clean Crypto-clean NetSSL_OpenSSL-clean Data-clean Data/SQLite-clean Data/ODBC-clean Data/MySQL-clean JWT-clean Zip-clean PageCompiler-clean PageCompiler/File2Page-clean CppParser-clean PDF-clean MongoDB-clean Redis-clean ActiveRecord-clean ActiveRecord/Compiler-clean
.PHONY: $(libexecs)
.PHONY: $(tests)
@ -374,6 +374,22 @@ Redis-clean:
$(MAKE) -C $(POCO_BASE)/Redis clean
$(MAKE) -C $(POCO_BASE)/Redis/testsuite clean
ActiveRecord-libexec: Foundation-libexec Data-libexec
$(MAKE) -C $(POCO_BASE)/ActiveRecord
ActiveRecord-tests: ActiveRecord-libexec Data/SQLite-libexec cppunit
$(MAKE) -C $(POCO_BASE)/ActiveRecord/testsuite
ActiveRecord-clean:
$(MAKE) -C $(POCO_BASE)/ActiveRecord clean
$(MAKE) -C $(POCO_BASE)/ActiveRecord/testsuite clean
ActiveRecord/Compiler-libexec: Foundation-libexec Util-libexec
$(MAKE) -C $(POCO_BASE)/ActiveRecord/Compiler
ActiveRecord/Compiler-clean:
$(MAKE) -C $(POCO_BASE)/ActiveRecord/Compiler clean
clean: cleans CppUnit-clean
distclean:

View File

@ -21,5 +21,6 @@ PDF
CppParser
MongoDB
Redis
ActiveRecord
PocoDoc
ProGen