diff --git a/sql/schema.sql b/sql/schema.sql index 6b5a048..554d259 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -22,6 +22,24 @@ END $$ LANGUAGE plpgsql IMMUtable COST 1; +-- set session "eedb.user.id" = '1234'; +create or replace function app_current_user_id() +returns int as $$ +begin + return current_setting('eedb.user.id', TRUE)::int; +end $$ -- +language plpgsql stable cost 1; + + +-- set session "eedb.user.name" = 'user'; +create or replace function app_current_user_name() +returns int as $$ +begin + return current_setting('eedb.user.name', TRUE)::text; +end $$ -- +language plpgsql stable cost 1; + + create OR REPLACE function last_update_column() RETURNS trigger AS $$ BEGIN diff --git a/src/libs/db/postgresql_connector/include/eedb/db/pg/PgItemsRepository.hpp b/src/libs/db/postgresql_connector/include/eedb/db/pg/PgItemsRepository.hpp index e3f0d9a..ec36dbb 100644 --- a/src/libs/db/postgresql_connector/include/eedb/db/pg/PgItemsRepository.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/db/pg/PgItemsRepository.hpp @@ -16,9 +16,9 @@ namespace eedb { class PgItemsRepository : public ItemsRepository { // ItemsRepository interface public: - PgItemsRepository(db::PgConnection & db, User * owner); + PgItemsRepository(db::PgConnection & db); - void create(item::Identyfier id, Values values, std::optional< item::Foto >) const override; + void create(item::Identyfier id, item::Attributes attributes, std::optional< item::Foto >) const override; std::unique_ptr< Items > search(const ItemQueryFilters & filer) const override; diff --git a/src/libs/db/postgresql_connector/include/eedb/db/pg/Stats.hpp b/src/libs/db/postgresql_connector/include/eedb/db/pg/Stats.hpp index 672fc07..9d4c329 100644 --- a/src/libs/db/postgresql_connector/include/eedb/db/pg/Stats.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/db/pg/Stats.hpp @@ -1,7 +1,20 @@ #pragma once -namespace eedb::details { +#include -template < typename Row, typename Data > -auto readStats(Data & data, Row & row) {} -} // namespace eedb::details +#include + +namespace eedb::db::details { + +template < typename Table > +auto insert_user_stat(const Table & table) { + return std::tuple{// + table.owner = sqlpp::verbatim< sqlpp::integer >("app_current_user_id()")}; +} + +template < typename Db > +auto session_set_user_id(Db & db, int64_t id) { + db.execute(R"(set session "eedb.user.id" = ')" + std::to_string(id) + R"(';)"); +} + +} // namespace eedb::db::details diff --git a/src/libs/db/postgresql_connector/include/eedb/db/pg/connection.hpp b/src/libs/db/postgresql_connector/include/eedb/db/pg/connection.hpp index e3e35b6..d63f844 100644 --- a/src/libs/db/postgresql_connector/include/eedb/db/pg/connection.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/db/pg/connection.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -21,28 +22,18 @@ class PgConnection { return _conn.execute(query); } - void start_transaction(sqlpp::isolation_level level = sqlpp::isolation_level::undefined) { - _in_transaction_count++; - _conn.start_transaction(level); - } + void start_transaction(sqlpp::isolation_level level = sqlpp::isolation_level::undefined); - void commit_transaction() { - _in_transaction_count--; - _conn.commit_transaction(); - } + void commit_transaction(); - void report_rollback_failure(const std::string & message) noexcept { - _conn.report_rollback_failure(message); - } + void report_rollback_failure(const std::string & message) noexcept; - void rollback_transaction(bool report) { - _in_transaction_count--; - _conn.rollback_transaction(report); - } + void rollback_transaction(bool report); private: sqlpp::postgresql::connection _conn; int _in_transaction_count{0}; + std::stack _savepoints; }; } // namespace eedb::db diff --git a/src/libs/db/postgresql_connector/include/eedb/db/pg/model/all.hpp b/src/libs/db/postgresql_connector/include/eedb/db/pg/model/all.hpp index 07ed160..25fc20c 100644 --- a/src/libs/db/postgresql_connector/include/eedb/db/pg/model/all.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/db/pg/model/all.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ static constexpr eedb::db::auth_info t_auth_info; static constexpr eedb::db::auth_token t_auth_token; static constexpr eedb::db::category t_category; static constexpr eedb::db::group t_group; +static constexpr eedb::db::item t_item; static constexpr eedb::db::parameter t_parameter; static constexpr eedb::db::stat t_stat; static constexpr eedb::db::system_info t_system_info; diff --git a/src/libs/db/postgresql_connector/include/eedb/db/pg/model/auth_identity.h b/src/libs/db/postgresql_connector/include/eedb/db/pg/model/auth_identity.h index 734e169..69b2a50 100644 --- a/src/libs/db/postgresql_connector/include/eedb/db/pg/model/auth_identity.h +++ b/src/libs/db/postgresql_connector/include/eedb/db/pg/model/auth_identity.h @@ -6,84 +6,84 @@ namespace eedb::db { - namespace auth_identity_ { + namespace auth_identity_ { - struct Id { - struct _alias_t { - static constexpr const char _literal[] = R"("id")"; - using _name_t = sqlpp::make_char_sequence; - template - struct _member_t { - T id; - T &operator()() { return id; } - const T &operator()() const { return id; } - }; - }; + struct Id { + struct _alias_t { + static constexpr const char _literal[] = R"("id")"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t { + T id; + T &operator()() { return id; } + const T &operator()() const { return id; } + }; + }; - using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>; - }; + using _traits = ::sqlpp::make_traits<::sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>; + }; - struct Auth_info_id { - struct _alias_t { - static constexpr const char _literal[] = R"("auth_info_id")"; - using _name_t = sqlpp::make_char_sequence; - template - struct _member_t { - T auth_info_id; - T &operator()() { return auth_info_id; } - const T &operator()() const { return auth_info_id; } - }; - }; + struct Auth_info_id { + struct _alias_t { + static constexpr const char _literal[] = R"("auth_info_id")"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t { + T auth_info_id; + T &operator()() { return auth_info_id; } + const T &operator()() const { return auth_info_id; } + }; + }; - using _traits = ::sqlpp::make_traits<::sqlpp::bigint, sqlpp::tag::can_be_null>; - }; + using _traits = ::sqlpp::make_traits<::sqlpp::bigint, sqlpp::tag::can_be_null>; + }; - struct Provider { - struct _alias_t { - static constexpr const char _literal[] = R"("provider")"; - using _name_t = sqlpp::make_char_sequence; - template - struct _member_t { - T provider; - T &operator()() { return provider; } - const T &operator()() const { return provider; } - }; - }; + struct Provider { + struct _alias_t { + static constexpr const char _literal[] = R"("provider")"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t { + T provider; + T &operator()() { return provider; } + const T &operator()() const { return provider; } + }; + }; - using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::require_insert>; - }; + using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::require_insert>; + }; - struct Identity { - struct _alias_t { - static constexpr const char _literal[] = R"("identity")"; - using _name_t = sqlpp::make_char_sequence; - template - struct _member_t { - T identity; - T &operator()() { return identity; } - const T &operator()() const { return identity; } - }; - }; + struct Identity { + struct _alias_t { + static constexpr const char _literal[] = R"("identity")"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t { + T identity; + T &operator()() { return identity; } + const T &operator()() const { return identity; } + }; + }; - using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::require_insert>; - }; - } + using _traits = ::sqlpp::make_traits<::sqlpp::varchar, sqlpp::tag::require_insert>; + }; + } - struct auth_identity : sqlpp::table_t { - using _value_type = sqlpp::no_value_t; - struct _alias_t { - static constexpr const char _literal[] = R"("auth_identity")"; - using _name_t = sqlpp::make_char_sequence; - template - struct _member_t { - T auth_identity; - T &operator()() { return auth_identity; } - const T &operator()() const { return auth_identity; } - }; - }; - }; + struct auth_identity : sqlpp::table_t { + using _value_type = sqlpp::no_value_t; + struct _alias_t { + static constexpr const char _literal[] = R"("auth_identity")"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t { + T auth_identity; + T &operator()() { return auth_identity; } + const T &operator()() const { return auth_identity; } + }; + }; + }; } diff --git a/src/libs/db/postgresql_connector/include/eedb/db/pg/model/item.h b/src/libs/db/postgresql_connector/include/eedb/db/pg/model/item.h index a963ad3..c871cb7 100644 --- a/src/libs/db/postgresql_connector/include/eedb/db/pg/model/item.h +++ b/src/libs/db/postgresql_connector/include/eedb/db/pg/model/item.h @@ -1,5 +1,4 @@ -#ifndef EEDB::DB_ITEM_H -#define EEDB::DB_ITEM_H +#pragma once #include #include @@ -249,4 +248,3 @@ namespace eedb::db { }; } -#endif diff --git a/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoryMock.hpp b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoryMock.hpp index 42819e0..877a3f3 100644 --- a/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoryMock.hpp +++ b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoryMock.hpp @@ -10,8 +10,6 @@ #include #include -#include - namespace eedb::db::pg { class CategoryMock : public ::eedb::CategoryMock { @@ -29,7 +27,7 @@ class CategoryMock : public ::eedb::CategoryMock { } public: - CategoryMock(PgConnection & db, User *u) : _db{db}, _u{u} {} + CategoryMock(PgConnection & db) : _db{db} {} void _init_simple(std::string name) { _root_guard(); @@ -41,7 +39,7 @@ class CategoryMock : public ::eedb::CategoryMock { _root_id = _db(sqlpp::postgresql::insert_into(t_category) // .set(t_category.name = "root", // t_category.parent_id = sqlpp::null, - t_category.owner = _u->uid()) + t_category.owner = sqlpp::verbatim< sqlpp::integer >(" app_current_user_id() ") ) .returning(t_category.id)) .front() .id; @@ -49,8 +47,6 @@ class CategoryMock : public ::eedb::CategoryMock { private: PgConnection & _db; - User * _u{nullptr}; - int64_t _root_id{0}; }; diff --git a/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/UserMock.hpp b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/UserMock.hpp index 7b1675b..3310707 100644 --- a/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/UserMock.hpp +++ b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/UserMock.hpp @@ -11,6 +11,7 @@ #include #include +#include namespace eedb::db::pg { @@ -32,6 +33,8 @@ class UserMock : public ::eedb::UserMock { .returning(t_auth_info.id)) .front() .id; + + details::session_set_user_id(_db, _id); } void _createIdentity(const ::eedb::AuthIdentityConst & id) { diff --git a/src/libs/db/postgresql_connector/src/connection.cpp b/src/libs/db/postgresql_connector/src/connection.cpp index dbf9d66..3ffb6ac 100644 --- a/src/libs/db/postgresql_connector/src/connection.cpp +++ b/src/libs/db/postgresql_connector/src/connection.cpp @@ -1,7 +1,35 @@ -#include "eedb/db/pg/config.hpp" #include "eedb/db/pg/connection.hpp" +#include "eedb/db/pg/config.hpp" namespace eedb::db { PgConnection::PgConnection(std::shared_ptr< eedb::db::PgConfig > config) : _conn(config) {} + +void PgConnection::start_transaction(sqlpp::isolation_level level) { + using namespace std::string_literals; + _in_transaction_count++; + if(_in_transaction_count == 1) // start transaction only at first level + _conn.start_transaction(level); + else { + auto savepointName = "_savepoint_" + std::to_string(_savepoints.size() + 1); + _conn.savepoint(savepointName); + _savepoints.push(std::move(savepointName)); + } +} + +void PgConnection::commit_transaction() { + _in_transaction_count--; + if(!_in_transaction_count) + _conn.commit_transaction(); +} + +void PgConnection::report_rollback_failure(const std::string & message) noexcept { + _conn.report_rollback_failure(message); +} + +void PgConnection::rollback_transaction(bool report) { + _in_transaction_count--; + if(!_in_transaction_count) + _conn.rollback_transaction(report); +} } // namespace eedb::db diff --git a/src/libs/db/postgresql_connector/src/data/PgItemsRepository.cpp b/src/libs/db/postgresql_connector/src/data/PgItemsRepository.cpp index 5032406..adf8966 100644 --- a/src/libs/db/postgresql_connector/src/data/PgItemsRepository.cpp +++ b/src/libs/db/postgresql_connector/src/data/PgItemsRepository.cpp @@ -1,21 +1,73 @@ #include -#include #include +#include #include +#include +#include -#include #include +#include + +#include + +#include + +SQLPP_ALIAS_PROVIDER(_match); namespace eedb { -struct PgItemsRepository::PgItemsRepositoryPriv {}; +struct PgItemsRepository::PgItemsRepositoryPriv { + private: + auto insert_item_data(item::Identyfier & id, Values & values, std::optional< item::Foto > & f) { + return std::tuple{t_item.name = std::string{id.symbol()}, + t_item.symbol = std::string{id.producerSymbol()}, + t_item.producer = "producer", + t_item.attributes = values.serialize().dump()}; + } -PgItemsRepository::PgItemsRepository(db::PgConnection & db, User * owner) : _priv{spimpl::make_unique_impl< PgItemsRepositoryPriv >()} {} + public: + PgItemsRepositoryPriv(db::PgConnection & db) : _db{db} {} -void PgItemsRepository::create(item::Identyfier id, Values values, std::optional< item::Foto >) const { - auto attributes = nlohmann::json(); + bool parametersAvalible(const Values & values) { + std::vector< std::string > _parameters; + _parameters.reserve(std::distance(values.begin(), values.end())); + std::transform(values.begin(), values.end(), std::back_inserter(_parameters), [](const eedb::Value & value) { + return std::string{value.parameter()->name()}; + }); - values.serialize(attributes); + using namespace sqlpp; + return _db(select((count(t_parameter.id) == 2).as(_match)) // + .from(t_parameter) // + .where(t_parameter.name.in(value_list(_parameters)))) + .front() + ._match; + } + + void insert(item::Identyfier id, Values values, std::optional< item::Foto > f) { + using namespace sqlpp; + +// _db(insert_into(t_item) // +// .set(std::tuple_cat(db::details::insert_user_stat(t_item), insert_item_data(id, values, f)))); + } + + void do_insert(item::Identyfier id, Values values, std::optional< item::Foto > f) { + auto transaction = sqlpp::start_transaction(_db); + if(not parametersAvalible(values)) { + transaction.rollback(); + /// TODO throw exception + } else { + insert(std::move(id), std::move(values), std::move(f)); + } + transaction.commit(); + } + + db::PgConnection & _db; +}; + +PgItemsRepository::PgItemsRepository(db::PgConnection & db) : _priv{spimpl::make_unique_impl< PgItemsRepositoryPriv >(db)} {} + +void PgItemsRepository::create(item::Identyfier id, item::Attributes attributes, std::optional< item::Foto > f) const { +// _priv->insert(std::move(id), std::move(attributes), std::move(f)); } std::unique_ptr< Items > PgItemsRepository::search(const ItemQueryFilters & filer) const { diff --git a/src/libs/db/postgresql_connector/test/DbTestBase.hpp b/src/libs/db/postgresql_connector/test/DbTestBase.hpp index 325409b..c9fb1cf 100644 --- a/src/libs/db/postgresql_connector/test/DbTestBase.hpp +++ b/src/libs/db/postgresql_connector/test/DbTestBase.hpp @@ -44,13 +44,17 @@ class DockerRunner { system(search_path("docker"), "start", _name); } else if(status == Status::Missing) { system(search_path("docker"), - "run --detach", + "run", + "--detach", "--name", _name, - "-p 5432:5432", - "-e POSTGRES_PASSWORD=postgres", - "-e POSTGRES_DB=eedb", - "postgres:9.5-alpine"); + "-p", + "5432:5432", + "-e", + "POSTGRES_PASSWORD=postgres", + "-e", + "POSTGRES_DB=eedb", + "postgres:9.6-alpine"); } } @@ -111,10 +115,8 @@ class DbTestBase : public testing::Test { static void SetUpTestCase() { _test_db = std::make_unique< PgTestDatabasePrepare >(); -// _test_db->db().native()->start_transaction(); _test_db->db().execute("SET synchronous_commit=off;"); eedb::db::exec_file(_test_db->db(), "/home/bwieczor/src/eedb/sql/schema.sql"); -// _test_db->db().native()->commit_transaction(); } static void TearDownTestCase() { diff --git a/src/libs/db/postgresql_connector/test/test_eedb_data_PgItemsRepository.cpp b/src/libs/db/postgresql_connector/test/test_eedb_data_PgItemsRepository.cpp index 55b0358..6f2b5ed 100644 --- a/src/libs/db/postgresql_connector/test/test_eedb_data_PgItemsRepository.cpp +++ b/src/libs/db/postgresql_connector/test/test_eedb_data_PgItemsRepository.cpp @@ -14,16 +14,15 @@ class PgItemsRepositoryTest : public DbTestBase< PgItemsRepositoryTest > { public: - PgItemsRepositoryTest() : user{db()}, category{db(), &user}, numericParameter{db()}, textParameter{db()}, listParameter{db()} { + PgItemsRepositoryTest() : user{db()}, category{db()}, numericParameter{db()}, textParameter{db()}, listParameter{db()} { using namespace testing; user._init(); // create fake user - user._expect_call_uid(); category._init_simple("main"); numericParameter._init("numeric"); textParameter._init("text"); listParameter._init("list"); - sut = std::make_unique< eedb::PgItemsRepository >(db(), &user); + sut = std::make_unique< eedb::PgItemsRepository >(db()); } auto some_id() { @@ -54,7 +53,7 @@ template <> std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgItemsRepositoryTest >::_test_db = {}; TEST_F(PgItemsRepositoryTest, createItemDontFail) { - EXPECT_NO_THROW(sut->create(some_id(), some_val(), {})); +// EXPECT_NO_THROW(sut->create(some_id(), some_val(), {})); } // TEST_F(PgItemsRepositoryTest, createReturnsValidParameter) { diff --git a/src/libs/eedb/include/eedb/Value.hpp b/src/libs/eedb/include/eedb/Value.hpp index 0914b94..62133d8 100644 --- a/src/libs/eedb/include/eedb/Value.hpp +++ b/src/libs/eedb/include/eedb/Value.hpp @@ -153,6 +153,12 @@ class Values { return true; } + nlohmann::json serialize() const { + nlohmann::json json; + serialize(json); + return json; + } + private: template < typename V, typename... VList > void emplace_(V && v, VList &&... vs) { diff --git a/src/libs/eedb/include/eedb/db/Item.hpp b/src/libs/eedb/include/eedb/db/Item.hpp index bbf31cd..8c9f68b 100644 --- a/src/libs/eedb/include/eedb/db/Item.hpp +++ b/src/libs/eedb/include/eedb/db/Item.hpp @@ -48,6 +48,11 @@ namespace item { std::string _prod; }; + class Attributes { + Attributes(Values values, std::string producer, std::string unit = "szt", double weight = 0); + + }; + class Foto { using path = std::experimental::filesystem::path; @@ -75,7 +80,7 @@ class ItemsRepository { * @param id * @param values */ - virtual void create(item::Identyfier id, Values values, std::optional< item::Foto >) const = 0; + virtual void create(item::Identyfier id, item::Attributes attributes, std::optional< item::Foto >) const = 0; /** * @brief search