mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-12 18:20:26 +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 "Enable PageCompiler" ON)
|
||||||
option(ENABLE_PAGECOMPILER_FILE2PAGE "Enable File2Page" 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
|
option(ENABLE_TESTS
|
||||||
"Set to OFF|ON (default is OFF) to control build of POCO tests & samples" OFF)
|
"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")
|
list(APPEND Poco_COMPONENTS "Redis")
|
||||||
endif()
|
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)
|
if(EXISTS ${PROJECT_SOURCE_DIR}/PDF AND ENABLE_PDF)
|
||||||
add_subdirectory(PDF)
|
add_subdirectory(PDF)
|
||||||
list(APPEND Poco_COMPONENTS "PDF")
|
list(APPEND Poco_COMPONENTS "PDF")
|
||||||
|
24
Makefile
24
Makefile
@ -78,7 +78,7 @@ poco: libexecs $(if $(TESTS),tests) $(if $(SAMPLES),samples)
|
|||||||
all: libexecs tests samples
|
all: libexecs tests samples
|
||||||
|
|
||||||
INSTALLDIR = $(DESTDIR)$(POCO_PREFIX)
|
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:
|
cppunit:
|
||||||
$(MAKE) -C $(POCO_BASE)/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 f -exec rm -f {} \;
|
||||||
find $(INSTALLDIR)/lib -name "libPoco*" -type l -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
|
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
|
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
|
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: $(libexecs)
|
||||||
.PHONY: $(tests)
|
.PHONY: $(tests)
|
||||||
@ -374,6 +374,22 @@ Redis-clean:
|
|||||||
$(MAKE) -C $(POCO_BASE)/Redis clean
|
$(MAKE) -C $(POCO_BASE)/Redis clean
|
||||||
$(MAKE) -C $(POCO_BASE)/Redis/testsuite 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
|
clean: cleans CppUnit-clean
|
||||||
|
|
||||||
distclean:
|
distclean:
|
||||||
|
@ -21,5 +21,6 @@ PDF
|
|||||||
CppParser
|
CppParser
|
||||||
MongoDB
|
MongoDB
|
||||||
Redis
|
Redis
|
||||||
|
ActiveRecord
|
||||||
PocoDoc
|
PocoDoc
|
||||||
ProGen
|
ProGen
|
||||||
|
Loading…
Reference in New Issue
Block a user