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
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
@@ -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
@@ -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>

@@ -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,
@@ -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);

5 changes: 5 additions & 0 deletions src/oatpp-postgresql/Executor.hpp
Original file line number Diff line number Diff line change
@@ -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;
42 changes: 41 additions & 1 deletion src/oatpp-postgresql/ql_template/Parser.cpp
Original file line number Diff line number Diff line change
@@ -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) {

@@ -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;

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

};

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

public:

struct CleanSection {
@@ -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:
5 changes: 3 additions & 2 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
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
@@ -4,6 +4,7 @@
#include "types/ArrayTest.hpp"
#include "types/IntTest.hpp"
#include "types/FloatTest.hpp"
#include "types/ContainerTest.hpp"
#include "types/InterpretationTest.hpp"


@@ -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);

}
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());
}

}

}}}}
16 changes: 16 additions & 0 deletions test/oatpp-postgresql/types/ContainerTest.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef oatpp_test_postgresql_types_ContainerTest_hpp
#define oatpp_test_postgresql_types_ContainerTest_hpp

#include "oatpp-test/UnitTest.hpp"

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

class ContainerTest : public UnitTest {
public:
ContainerTest() : UnitTest("TEST[postgresql::types::ContainerTest]") {}
void onRun() override;
};

}}}}

#endif // oatpp_test_postgresql_types_ContainerTest_hpp
2 changes: 1 addition & 1 deletion test/oatpp-postgresql/types/IntTest.cpp
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
* limitations under the License.
*
***************************************************************************/

#define NOMINMAX
#include "IntTest.hpp"

#include "oatpp-postgresql/orm.hpp"