mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-12 10:13:51 +01:00
added ActiveRecord library and compiler
This commit is contained in:
parent
9dfda83305
commit
22e762dd9a
37
ActiveRecord/CMakeLists.txt
Normal file
37
ActiveRecord/CMakeLists.txt
Normal 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()
|
19
ActiveRecord/Compiler/CMakeLists.txt
Normal file
19
ActiveRecord/Compiler/CMakeLists.txt
Normal 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
|
||||
)
|
14
ActiveRecord/Compiler/Makefile
Normal file
14
ActiveRecord/Compiler/Makefile
Normal 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
|
166
ActiveRecord/Compiler/src/CodeGenerator.cpp
Normal file
166
ActiveRecord/Compiler/src/CodeGenerator.cpp
Normal 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
|
71
ActiveRecord/Compiler/src/CodeGenerator.h
Normal file
71
ActiveRecord/Compiler/src/CodeGenerator.h
Normal 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
|
220
ActiveRecord/Compiler/src/Compiler.cpp
Normal file
220
ActiveRecord/Compiler/src/Compiler.cpp
Normal 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)
|
351
ActiveRecord/Compiler/src/HeaderGenerator.cpp
Normal file
351
ActiveRecord/Compiler/src/HeaderGenerator.cpp
Normal 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
|
59
ActiveRecord/Compiler/src/HeaderGenerator.h
Normal file
59
ActiveRecord/Compiler/src/HeaderGenerator.h
Normal 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
|
418
ActiveRecord/Compiler/src/ImplGenerator.cpp
Normal file
418
ActiveRecord/Compiler/src/ImplGenerator.cpp
Normal 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
|
56
ActiveRecord/Compiler/src/ImplGenerator.h
Normal file
56
ActiveRecord/Compiler/src/ImplGenerator.h
Normal 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
|
277
ActiveRecord/Compiler/src/Parser.cpp
Normal file
277
ActiveRecord/Compiler/src/Parser.cpp
Normal 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
|
63
ActiveRecord/Compiler/src/Parser.h
Normal file
63
ActiveRecord/Compiler/src/Parser.h
Normal 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
|
60
ActiveRecord/Compiler/src/Types.h
Normal file
60
ActiveRecord/Compiler/src/Types.h
Normal 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
15
ActiveRecord/Makefile
Normal 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
|
4
ActiveRecord/cmake/PocoActiveRecordConfig.cmake
Normal file
4
ActiveRecord/cmake/PocoActiveRecordConfig.cmake
Normal file
@ -0,0 +1,4 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(PocoFoundation)
|
||||
find_dependency(PocoData)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/PocoActiveRecordTargets.cmake")
|
278
ActiveRecord/include/Poco/ActiveRecord/ActiveRecord.h
Normal file
278
ActiveRecord/include/Poco/ActiveRecord/ActiveRecord.h
Normal 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
|
58
ActiveRecord/include/Poco/ActiveRecord/ActiveRecordLib.h
Normal file
58
ActiveRecord/include/Poco/ActiveRecord/ActiveRecordLib.h
Normal 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
|
74
ActiveRecord/include/Poco/ActiveRecord/Context.h
Normal file
74
ActiveRecord/include/Poco/ActiveRecord/Context.h
Normal 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
|
183
ActiveRecord/include/Poco/ActiveRecord/IDTraits.h
Normal file
183
ActiveRecord/include/Poco/ActiveRecord/IDTraits.h
Normal 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
|
197
ActiveRecord/include/Poco/ActiveRecord/Query.h
Normal file
197
ActiveRecord/include/Poco/ActiveRecord/Query.h
Normal 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
|
@ -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
|
70
ActiveRecord/include/Poco/ActiveRecord/TypeHandler.h
Normal file
70
ActiveRecord/include/Poco/ActiveRecord/TypeHandler.h
Normal 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
|
||||
|
62
ActiveRecord/src/ActiveRecord.cpp
Normal file
62
ActiveRecord/src/ActiveRecord.cpp
Normal 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
|
43
ActiveRecord/src/Context.cpp
Normal file
43
ActiveRecord/src/Context.cpp
Normal 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
|
32
ActiveRecord/src/IDTraits.cpp
Normal file
32
ActiveRecord/src/IDTraits.cpp
Normal 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
|
54
ActiveRecord/src/StatementPlaceholderProvider.cpp
Normal file
54
ActiveRecord/src/StatementPlaceholderProvider.cpp
Normal 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
|
36
ActiveRecord/testsuite/CMakeLists.txt
Normal file
36
ActiveRecord/testsuite/CMakeLists.txt
Normal 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
|
||||
)
|
16
ActiveRecord/testsuite/Makefile
Normal file
16
ActiveRecord/testsuite/Makefile
Normal 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
|
15
ActiveRecord/testsuite/ORM.xml
Normal file
15
ActiveRecord/testsuite/ORM.xml
Normal 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>
|
161
ActiveRecord/testsuite/include/ORM/Employee.h
Normal file
161
ActiveRecord/testsuite/include/ORM/Employee.h
Normal 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
|
116
ActiveRecord/testsuite/include/ORM/Role.h
Normal file
116
ActiveRecord/testsuite/include/ORM/Role.h
Normal 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
|
333
ActiveRecord/testsuite/src/ActiveRecordTest.cpp
Normal file
333
ActiveRecord/testsuite/src/ActiveRecordTest.cpp
Normal 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;
|
||||
}
|
51
ActiveRecord/testsuite/src/ActiveRecordTest.h
Normal file
51
ActiveRecord/testsuite/src/ActiveRecordTest.h
Normal 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
|
22
ActiveRecord/testsuite/src/ActiveRecordTestSuite.cpp
Normal file
22
ActiveRecord/testsuite/src/ActiveRecordTestSuite.cpp
Normal 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;
|
||||
}
|
27
ActiveRecord/testsuite/src/ActiveRecordTestSuite.h
Normal file
27
ActiveRecord/testsuite/src/ActiveRecordTestSuite.h
Normal 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
|
17
ActiveRecord/testsuite/src/Driver.cpp
Normal file
17
ActiveRecord/testsuite/src/Driver.cpp
Normal 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)
|
151
ActiveRecord/testsuite/src/Employee.cpp
Normal file
151
ActiveRecord/testsuite/src/Employee.cpp
Normal 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
|
109
ActiveRecord/testsuite/src/Role.cpp
Normal file
109
ActiveRecord/testsuite/src/Role.cpp
Normal 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
|
30
ActiveRecord/testsuite/src/WinCEDriver.cpp
Normal file
30
ActiveRecord/testsuite/src/WinCEDriver.cpp
Normal 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;
|
||||
}
|
28
ActiveRecord/testsuite/src/WinDriver.cpp
Normal file
28
ActiveRecord/testsuite/src/WinDriver.cpp
Normal 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;
|
@ -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")
|
||||
|
62
Makefile
62
Makefile
@ -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)
|
||||
@ -144,7 +144,7 @@ Foundation-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Foundation/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/Foundation/samples clean
|
||||
|
||||
Encodings-libexec: Foundation-libexec
|
||||
Encodings-libexec: Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Encodings
|
||||
|
||||
Encodings-tests: Encodings-libexec cppunit
|
||||
@ -158,7 +158,7 @@ Encodings-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Encodings/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/Encodings/samples clean
|
||||
|
||||
XML-libexec: Foundation-libexec
|
||||
XML-libexec: Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/XML
|
||||
|
||||
XML-tests: XML-libexec cppunit
|
||||
@ -172,7 +172,7 @@ XML-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/XML/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/XML/samples clean
|
||||
|
||||
JSON-libexec: Foundation-libexec
|
||||
JSON-libexec: Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/JSON
|
||||
|
||||
JSON-tests: JSON-libexec cppunit
|
||||
@ -186,7 +186,7 @@ JSON-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/JSON/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/JSON/samples clean
|
||||
|
||||
Util-libexec: Foundation-libexec XML-libexec JSON-libexec
|
||||
Util-libexec: Foundation-libexec XML-libexec JSON-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Util
|
||||
|
||||
Util-tests: Util-libexec cppunit
|
||||
@ -200,7 +200,7 @@ Util-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Util/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/Util/samples clean
|
||||
|
||||
Net-libexec: Foundation-libexec
|
||||
Net-libexec: Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Net
|
||||
|
||||
Net-tests: Net-libexec cppunit
|
||||
@ -214,7 +214,7 @@ Net-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Net/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/Net/samples clean
|
||||
|
||||
Crypto-libexec: Foundation-libexec
|
||||
Crypto-libexec: Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Crypto
|
||||
|
||||
Crypto-tests: Crypto-libexec cppunit
|
||||
@ -228,7 +228,7 @@ Crypto-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Crypto/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/Crypto/samples clean
|
||||
|
||||
NetSSL_OpenSSL-libexec: Foundation-libexec Net-libexec Util-libexec Crypto-libexec
|
||||
NetSSL_OpenSSL-libexec: Foundation-libexec Net-libexec Util-libexec Crypto-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/NetSSL_OpenSSL
|
||||
|
||||
NetSSL_OpenSSL-tests: NetSSL_OpenSSL-libexec cppunit
|
||||
@ -242,7 +242,7 @@ NetSSL_OpenSSL-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/NetSSL_OpenSSL/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/NetSSL_OpenSSL/samples clean
|
||||
|
||||
Data-libexec: Foundation-libexec
|
||||
Data-libexec: Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Data
|
||||
|
||||
Data-tests: Data-libexec cppunit
|
||||
@ -256,7 +256,7 @@ Data-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Data/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/Data/samples clean
|
||||
|
||||
Data/SQLite-libexec: Foundation-libexec Data-libexec
|
||||
Data/SQLite-libexec: Foundation-libexec Data-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Data/SQLite
|
||||
|
||||
Data/SQLite-tests: Data/SQLite-libexec cppunit
|
||||
@ -266,7 +266,7 @@ Data/SQLite-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Data/SQLite clean
|
||||
$(MAKE) -C $(POCO_BASE)/Data/SQLite/testsuite clean
|
||||
|
||||
Data/ODBC-libexec: Foundation-libexec Data-libexec
|
||||
Data/ODBC-libexec: Foundation-libexec Data-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Data/ODBC
|
||||
|
||||
Data/ODBC-tests: Data/ODBC-libexec cppunit
|
||||
@ -276,7 +276,7 @@ Data/ODBC-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Data/ODBC clean
|
||||
$(MAKE) -C $(POCO_BASE)/Data/ODBC/testsuite clean
|
||||
|
||||
Data/MySQL-libexec: Foundation-libexec Data-libexec
|
||||
Data/MySQL-libexec: Foundation-libexec Data-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Data/MySQL
|
||||
|
||||
Data/MySQL-tests: Data/MySQL-libexec cppunit
|
||||
@ -286,7 +286,7 @@ Data/MySQL-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Data/MySQL clean
|
||||
$(MAKE) -C $(POCO_BASE)/Data/MySQL/testsuite clean
|
||||
|
||||
Zip-libexec: Foundation-libexec Net-libexec Util-libexec XML-libexec
|
||||
Zip-libexec: Foundation-libexec Net-libexec Util-libexec XML-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Zip
|
||||
|
||||
Zip-tests: Zip-libexec cppunit
|
||||
@ -300,7 +300,7 @@ Zip-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/Zip/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/Zip/samples clean
|
||||
|
||||
PageCompiler-libexec: Net-libexec Util-libexec XML-libexec Foundation-libexec
|
||||
PageCompiler-libexec: Net-libexec Util-libexec XML-libexec Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/PageCompiler
|
||||
|
||||
PageCompiler-samples: PageCompiler-libexec
|
||||
@ -310,13 +310,13 @@ PageCompiler-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/PageCompiler clean
|
||||
$(MAKE) -C $(POCO_BASE)/PageCompiler/samples clean
|
||||
|
||||
PageCompiler/File2Page-libexec: Net-libexec Util-libexec XML-libexec Foundation-libexec
|
||||
PageCompiler/File2Page-libexec: Net-libexec Util-libexec XML-libexec Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/PageCompiler/File2Page
|
||||
|
||||
PageCompiler/File2Page-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/PageCompiler/File2Page clean
|
||||
|
||||
JWT-libexec: Foundation-libexec JSON-libexec Crypto-libexec
|
||||
JWT-libexec: Foundation-libexec JSON-libexec Crypto-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/JWT
|
||||
|
||||
JWT-tests: JWT-libexec cppunit
|
||||
@ -326,7 +326,7 @@ JWT-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/JWT clean
|
||||
$(MAKE) -C $(POCO_BASE)/JWT/testsuite clean
|
||||
|
||||
CppParser-libexec: Foundation-libexec
|
||||
CppParser-libexec: Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/CppParser
|
||||
|
||||
CppParser-tests: CppParser-libexec cppunit
|
||||
@ -336,7 +336,7 @@ CppParser-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/CppParser clean
|
||||
$(MAKE) -C $(POCO_BASE)/CppParser/testsuite clean
|
||||
|
||||
PDF-libexec: Util-libexec XML-libexec JSON-libexec Foundation-libexec
|
||||
PDF-libexec: Util-libexec XML-libexec JSON-libexec Foundation-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/PDF
|
||||
|
||||
PDF-tests: PDF-libexec cppunit
|
||||
@ -350,7 +350,7 @@ PDF-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/PDF/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/PDF/samples clean
|
||||
|
||||
MongoDB-libexec: Foundation-libexec Net-libexec
|
||||
MongoDB-libexec: Foundation-libexec Net-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/MongoDB
|
||||
|
||||
MongoDB-tests: MongoDB-libexec cppunit
|
||||
@ -364,7 +364,7 @@ MongoDB-clean:
|
||||
$(MAKE) -C $(POCO_BASE)/MongoDB/testsuite clean
|
||||
$(MAKE) -C $(POCO_BASE)/MongoDB/samples clean
|
||||
|
||||
Redis-libexec: Foundation-libexec Net-libexec
|
||||
Redis-libexec: Foundation-libexec Net-libexec
|
||||
$(MAKE) -C $(POCO_BASE)/Redis
|
||||
|
||||
Redis-tests: Redis-libexec cppunit
|
||||
@ -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:
|
||||
|
@ -21,5 +21,6 @@ PDF
|
||||
CppParser
|
||||
MongoDB
|
||||
Redis
|
||||
ActiveRecord
|
||||
PocoDoc
|
||||
ProGen
|
||||
|
Loading…
Reference in New Issue
Block a user