Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for container CRUD operations, tested. #10

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ build/

**/.DS_Store

/.vs
/CMakePresets.json
68 changes: 68 additions & 0 deletions src/oatpp-postgresql/Executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

#include "oatpp/core/data/stream/ChunkedBuffer.hpp"
#include "oatpp/core/macro/codegen.hpp"
#include "oatpp/core/utils/ConversionUtils.hpp"

#include <vector>

Expand Down Expand Up @@ -261,6 +262,67 @@ std::shared_ptr<QueryResult> Executor::executeQuery(const StringTemplate& queryT

}

template<class ContainerPtr>
std::string expandQuery(ContainerPtr container,
const std::shared_ptr<ql_template::Parser::ArrayVariableExtra>& a_extra,
std::unordered_map<String,Void>& params,
orm::Executor::ParamsTypeMap& paramsTypeMap)
{
std::string values;
bool is_first = true;
int index = 0;
for(const Void& entity : *container)
{
values += std::string(is_first ? "" : ",") + "(";
is_first = true;
String entity_name = String("array_value") + utils::conversion::int32ToStr(index++);
if(a_extra->variables.empty())
{
values += std::string(is_first ? "" : ",") + ":" + entity_name->c_str();
is_first = false;
}
else{
for(const auto& member : a_extra->variables)
{
values += std::string(is_first ? "" : ",") + ":" + entity_name->c_str() + "." + member.name->c_str();
is_first = false;
}
}
params.insert({entity_name, entity});
paramsTypeMap.insert({entity_name, entity.valueType});
values += ")";
}
return values;
}

std::shared_ptr<QueryResult> Executor::expandAndExecuteQuery(const StringTemplate& queryTemplate,
const std::unordered_map<oatpp::String, oatpp::Void>& params,
const std::shared_ptr<const data::mapping::TypeResolver>& typeResolver,
const std::shared_ptr<postgresql::Connection>& connection)
{
const auto& extra = std::static_pointer_cast<ql_template::Parser::TemplateExtra>(queryTemplate.getExtraData());
const auto& var = queryTemplate.getTemplateVariables()[0];
const auto& it = params.at(var.name);
const auto& array_extra = std::static_pointer_cast<ql_template::Parser::ArrayVariableExtra>(var.extra);

std::unordered_map<String,Void> new_params;
orm::Executor::ParamsTypeMap paramsTypeMap;
std::string values;

if(it.valueType->classId.id == oatpp::Vector<Void>::Class::CLASS_ID.id)
values = expandQuery(it.staticCast<Vector<Void>>(), array_extra,new_params,paramsTypeMap);
else if(it.valueType->classId.id == oatpp::List<Void>::Class::CLASS_ID.id)
values = expandQuery(it.staticCast<List<Void>>(), array_extra,new_params,paramsTypeMap);
else if(it.valueType->classId.id == oatpp::UnorderedSet<Void>::Class::CLASS_ID.id)
values = expandQuery(it.staticCast<UnorderedSet<Void>>(), array_extra,new_params,paramsTypeMap);

const std::string& prepared_query = extra->preparedTemplate->std_str();
const String& query = (prepared_query.substr(0, var.posStart) + values + prepared_query.substr(var.posStart + 2)).c_str();
const auto& newQueryTemplate = parseQueryTemplate(extra->templateName + utils::conversion::uint64ToStr(new_params.size()), query, paramsTypeMap, extra->prepare);

return executeQuery(newQueryTemplate, new_params, typeResolver, connection);
}

data::share::StringTemplate Executor::parseQueryTemplate(const oatpp::String& name,
const oatpp::String& text,
const ParamsTypeMap& paramsTypeMap,
Expand Down Expand Up @@ -325,6 +387,12 @@ std::shared_ptr<orm::QueryResult> Executor::execute(const StringTemplate& queryT
return executeQueryPrepared(queryTemplate, params, tr, pgConnection);

}
if (queryTemplate.getTemplateVariables().size() == 1)
{
const auto& var = queryTemplate.getTemplateVariables()[0];
if (std::static_pointer_cast<ql_template::Parser::ArrayVariableExtra>(var.extra) != nullptr)
return expandAndExecuteQuery(queryTemplate,params,tr,pgConnection);
}

return executeQuery(queryTemplate, params, tr, pgConnection);

Expand Down
5 changes: 5 additions & 0 deletions src/oatpp-postgresql/Executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ class Executor : public orm::Executor {
const std::shared_ptr<const data::mapping::TypeResolver>& typeResolver,
const std::shared_ptr<postgresql::Connection>& connection);

std::shared_ptr<QueryResult> expandAndExecuteQuery(const StringTemplate& queryTemplate,
const std::unordered_map<oatpp::String, oatpp::Void>& params,
const std::shared_ptr<const data::mapping::TypeResolver>& typeResolver,
const std::shared_ptr<postgresql::Connection>& connection);

private:
std::shared_ptr<provider::Provider<Connection>> m_connectionProvider;
std::shared_ptr<mapping::ResultMapper> m_resultMapper;
Expand Down
42 changes: 41 additions & 1 deletion src/oatpp-postgresql/ql_template/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,39 @@ data::share::StringTemplate::Variable Parser::parseIdentifier(parser::Caret& car
result.posEnd = caret.getPosition() - 1;
return result;
}
data::share::StringTemplate::Variable Parser::parseArrayIdentifier(parser::Caret& caret) {
data::share::StringTemplate::Variable result;
auto vars = std::make_shared<ArrayVariableExtra>();
result.posStart = caret.getPosition();
if(caret.canContinueAtChar('%', 1)) {
auto label = caret.putLabel();
while(caret.canContinue()) {
v_char8 a = *caret.getCurrData();
bool isAllowedChar = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || (a >= '0' && a <= '9') || (a == '_') || (a == '.');
if(!isAllowedChar) {
result.name = label.toString();
break;
}
caret.inc();
}
while(!caret.isAtChar('%')) {
if(caret.isAtChar(':'))
{
auto var = parseIdentifier(caret);
if(var.name) {
vars->variables.push_back(var);
}
}
caret.inc();
}
} else {
caret.setError("Invalid identifier");
}
result.extra = std::move(vars);
result.posEnd = caret.getPosition();
caret.inc();
return result;
}

void Parser::skipStringInQuotes(parser::Caret& caret) {

Expand Down Expand Up @@ -203,7 +236,14 @@ data::share::StringTemplate Parser::parseTemplate(const oatpp::String& text) {
}
}
break;

case '%':
{
auto var = parseArrayIdentifier(caret);
if(var.name) {
variables.push_back(var);
}
}
break;
case '\'': skipStringInQuotes(caret); break;
case '$': skipStringInDollars(caret); break;

Expand Down
5 changes: 5 additions & 0 deletions src/oatpp-postgresql/ql_template/Parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ class Parser {

};

struct ArrayVariableExtra {
std::vector<oatpp::data::share::StringTemplate::Variable> variables;
};

public:

struct CleanSection {
Expand All @@ -78,6 +82,7 @@ class Parser {

private:
static data::share::StringTemplate::Variable parseIdentifier(parser::Caret& caret);
static data::share::StringTemplate::Variable parseArrayIdentifier(parser::Caret& caret);
static void skipStringInQuotes(parser::Caret& caret);
static void skipStringInDollars(parser::Caret& caret);
public:
Expand Down
5 changes: 3 additions & 2 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ add_executable(module-tests
oatpp-postgresql/types/InterpretationTest.hpp
oatpp-postgresql/types/IntTest.cpp
oatpp-postgresql/types/IntTest.hpp
oatpp-postgresql/tests.cpp
)
oatpp-postgresql/types/ContainerTest.hpp
oatpp-postgresql/types/ContainerTest.cpp
oatpp-postgresql/tests.cpp)

set_target_properties(module-tests PROPERTIES
CXX_STANDARD 11
Expand Down
7 changes: 7 additions & 0 deletions test/oatpp-postgresql/migration/ContainerTest.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DROP TABLE IF EXISTS test_user;


CREATE TABLE test_user (
username text,
pass text
);
2 changes: 2 additions & 0 deletions test/oatpp-postgresql/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "types/ArrayTest.hpp"
#include "types/IntTest.hpp"
#include "types/FloatTest.hpp"
#include "types/ContainerTest.hpp"
#include "types/InterpretationTest.hpp"


Expand Down Expand Up @@ -39,6 +40,7 @@ void runTests() {
OATPP_RUN_TEST(oatpp::test::postgresql::types::IntTest);
OATPP_RUN_TEST(oatpp::test::postgresql::types::FloatTest);
OATPP_RUN_TEST(oatpp::test::postgresql::types::ArrayTest);
OATPP_RUN_TEST(oatpp::test::postgresql::types::ContainerTest);
OATPP_RUN_TEST(oatpp::test::postgresql::types::InterpretationTest);

}
Expand Down
179 changes: 179 additions & 0 deletions test/oatpp-postgresql/types/ContainerTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#include "ContainerTest.hpp"

#include "oatpp-postgresql/orm.hpp"
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "oatpp/core/utils/ConversionUtils.hpp"

namespace oatpp { namespace test { namespace postgresql { namespace types {

namespace {

#include OATPP_CODEGEN_BEGIN(DTO)

class User : public oatpp::DTO {

DTO_INIT(User, DTO)

DTO_FIELD(String, username);
DTO_FIELD(String, pass);

};

#include OATPP_CODEGEN_END(DTO)

bool operator==(const Object<User>& us1, const Object<User>& us2)
{
return us1->username == us2->username && us1->pass == us2->pass;
}

#include OATPP_CODEGEN_BEGIN(DbClient)

class MyClient : public oatpp::orm::DbClient {
public:

MyClient(const std::shared_ptr<oatpp::orm::Executor>& executor)
: oatpp::orm::DbClient(executor)
{

executeQuery("DROP TABLE IF EXISTS oatpp_schema_version_ContainerTest;", {});

oatpp::orm::SchemaMigration migration(executor, "ContainerTest");
migration.addFile(1, TEST_DB_MIGRATION "ContainerTest.sql");
migration.migrate();

auto version = executor->getSchemaVersion("ContainerTest");
OATPP_LOGD("DbClient", "Migration - OK. Version=%d.", version);

}

QUERY(insert_all,
"INSERT INTO test_user (username,pass) VALUES"
" %users (:username, :pass)% RETURNING *;",
PARAM(oatpp::Vector<oatpp::Object<User>>, users), PREPARE(false));

QUERY(update_all,
"UPDATE test_user as target SET pass=source.pass FROM (VALUES %users (:username, :pass)%) "
"as source(username, pass) WHERE target.username = source.username RETURNING target.*;",
PARAM(oatpp::Vector<oatpp::Object<User>>, users), PREPARE(false));

QUERY(find_all,
"SELECT * FROM test_user WHERE username IN (%users%)",
PARAM(oatpp::Vector<String>, users), PREPARE(false));

QUERY(delete_all,
"DELETE FROM test_user WHERE username IN (%users%)",
PARAM(oatpp::Vector<String>, users), PREPARE(false));

};

#include OATPP_CODEGEN_END(DbClient)

}

void ContainerTest::onRun() {

OATPP_LOGI(TAG, "DB-URL='%s'", TEST_DB_URL);

auto connectionProvider = std::make_shared<oatpp::postgresql::ConnectionProvider>(TEST_DB_URL);
const auto& executor = std::make_shared<oatpp::postgresql::Executor>(connectionProvider);

auto client = MyClient(executor);

{
auto users = Vector<Object<User>>::createShared();
for(size_t i = 0; i < 3; ++i)
{
auto o = Object<User>::createShared();
o->username = "test_" + utils::conversion::uint64ToStr(i);
o->pass = "pass_" + utils::conversion::uint64ToStr(i);
users->push_back(o);
}
auto res = client.insert_all(users);
OATPP_ASSERT(res->isSuccess())
const auto& data = res->fetch<Vector<Object<User>>>();
for(size_t i = 0; i < users->size(); ++i)
{
OATPP_ASSERT(users[i] == data[i])
}
client.executeQuery("DELETE FROM test_user",{});
}

{
auto users = Vector<Object<User>>::createShared();
for(size_t i = 0; i < 3; ++i)
{
auto o = Object<User>::createShared();
o->username = "test_" + utils::conversion::uint64ToStr(i);
o->pass = "pass_" + utils::conversion::uint64ToStr(i);
users->push_back(o);
}
auto res = client.insert_all(users);
OATPP_ASSERT(res->isSuccess())

for(size_t i = 0; i < users->size(); ++i)
{
users[i]->pass = "changed_pass_" + utils::conversion::uint64ToStr(i);
}
res = client.update_all(users);
OATPP_ASSERT(res->isSuccess())
const auto& data = res->fetch<Vector<Object<User>>>();
for(size_t i = 0; i < users->size(); ++i)
{
OATPP_ASSERT(users[i] == data[i])
}
client.executeQuery("DELETE FROM test_user",{});
}

{
auto users = Vector<Object<User>>::createShared();
for(size_t i = 0; i < 3; ++i)
{
auto o = Object<User>::createShared();
o->username = "test_" + utils::conversion::uint64ToStr(i);
o->pass = "pass_" + utils::conversion::uint64ToStr(i);
users->push_back(o);
}
auto res = client.insert_all(users);
OATPP_ASSERT(res->isSuccess())

auto usernames = Vector<String>::createShared();
for(size_t i = 0; i < users->size(); ++i)
{
usernames->push_back(users[i]->username);
}
res = client.find_all(usernames);
OATPP_ASSERT(res->isSuccess())
const auto& data = res->fetch<Vector<Object<User>>>();
for(size_t i = 0; i < users->size(); ++i)
{
OATPP_ASSERT(users[i] == data[i])
}
client.executeQuery("DELETE FROM test_user",{});
}

{
auto users = Vector<Object<User>>::createShared();
for(size_t i = 0; i < 3; ++i)
{
auto o = Object<User>::createShared();
o->username = "test_" + utils::conversion::uint64ToStr(i);
o->pass = "pass_" + utils::conversion::uint64ToStr(i);
users->push_back(o);
}
auto res = client.insert_all(users);
OATPP_ASSERT(res->isSuccess())

auto usernames = Vector<String>::createShared();
for(size_t i = 0; i < users->size(); ++i)
{
usernames->push_back(users[i]->username);
}
res = client.delete_all(usernames);
OATPP_ASSERT(res->isSuccess())
const auto& data = res->fetch<Vector<Object<User>>>();
OATPP_ASSERT(data->empty());
}

}

}}}}
Loading