Compare commits
375
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24677d61df | ||
|
|
5da70ba3a5 | ||
|
|
c880728093 | ||
|
|
78b30f9742 | ||
|
|
b19d0fd3ca | ||
|
|
730ab05213 | ||
|
|
6ecda781f5 | ||
|
|
d4865e4d10 | ||
|
|
06f6166f05 | ||
|
|
89b795c488 | ||
|
|
1b98eb4019 | ||
|
|
47fd6d68e4 | ||
|
|
34a5cfcc88 | ||
|
|
705d361aaa | ||
|
|
87bd2f8b89 | ||
|
|
8914c06833 | ||
|
|
34a9a8aa4a | ||
|
|
12f8590634 | ||
|
|
cea7b97f2b | ||
|
|
bd06034ebf | ||
|
|
68d67b17b7 | ||
|
|
73bd6f641c | ||
|
|
98d58d8e60 | ||
|
|
b22a830d18 | ||
|
|
85e6ee4b93 | ||
|
|
2e1b583741 | ||
|
|
6701031007 | ||
|
|
57f57956de | ||
|
|
141d798a41 | ||
|
|
cc0bcbeb61 | ||
|
|
5ec7c13420 | ||
|
|
ddf19bbf07 | ||
|
|
194e139e8b | ||
|
|
6629a71f6e | ||
|
|
72019bc1e7 | ||
|
|
7216f07c9f | ||
|
|
aa8da1f76f | ||
|
|
627af1d1a8 | ||
|
|
0f3028c5c7 | ||
|
|
25405de2e0 | ||
|
|
e58b7b9b0d | ||
|
|
052ff657c1 | ||
|
|
08a28b1627 | ||
|
|
60f03246dc | ||
|
|
caacdfa34a | ||
|
|
8c8423bf64 | ||
|
|
820b5432b2 | ||
|
|
bf99642af4 | ||
|
|
9f54716df2 | ||
|
|
57703dfb67 | ||
|
|
22f6f71412 | ||
|
|
1cdd83cb31 | ||
|
|
e3618c60e3 | ||
|
|
ceb7795d9c | ||
|
|
68eb2b6a6e | ||
|
|
a714cd2a53 | ||
|
|
1e08996087 | ||
|
|
5e6ba3666e | ||
|
|
3d4a61749b | ||
|
|
3658664eb9 | ||
|
|
8753042b3c | ||
|
|
85d297995d | ||
|
|
e6adffc264 | ||
|
|
95cd363097 | ||
|
|
9c37e989ba | ||
|
|
e7523e2bba | ||
|
|
37855dc6fb | ||
|
|
433b0e40e1 | ||
|
|
3fb23e167e | ||
|
|
8e1dbe2ccd | ||
|
|
96c6c78cb8 | ||
|
|
1a22c70320 | ||
|
|
75d5cba6c3 | ||
|
|
656d045ccf | ||
|
|
3a82f4891a | ||
|
|
a34a3dc266 | ||
|
|
b6456356b4 | ||
|
|
9b25f7f556 | ||
|
|
78eb5d04cd | ||
|
|
59e1533cca | ||
|
|
f144b26dbb | ||
|
|
d734d647ed | ||
|
|
0a0e3752ea | ||
|
|
a0326632ab | ||
|
|
f03664d60a | ||
|
|
53ac6810d9 | ||
|
|
f62f3e626b | ||
|
|
d59b41d06c | ||
|
|
57246d8742 | ||
|
|
1806e2670c | ||
|
|
9e4660c4ee | ||
|
|
f4e984a288 | ||
|
|
855c9295fb | ||
|
|
40dfc30dae | ||
|
|
920fdc68ed | ||
|
|
f751541bdd | ||
|
|
833cb64352 | ||
|
|
597965b62f | ||
|
|
fedce38270 | ||
|
|
3fc08fb7ff | ||
|
|
ee8ce62687 | ||
|
|
905ee019d7 | ||
|
|
907bcf90a6 | ||
|
|
6b2db17f58 | ||
|
|
6423bd505b | ||
|
|
7996608f62 | ||
|
|
53b10fd1f9 | ||
|
|
86c9e6b1cd | ||
|
|
6bdf281eab | ||
|
|
801fbdd187 | ||
|
|
2aff0cabbb | ||
|
|
9b4738a5ff | ||
|
|
bc8158a6ca | ||
|
|
d9eaa2009f | ||
|
|
85c56584be | ||
|
|
1e78430855 | ||
|
|
626eae1ac8 | ||
|
|
0157233b02 | ||
|
|
cab3e508bb | ||
|
|
d50a8f2b76 | ||
|
|
e45c38b229 | ||
|
|
a2c5bca709 | ||
|
|
cfd2a9c423 | ||
|
|
1deaab116c | ||
|
|
02d6ac514c | ||
|
|
4377f2fab1 | ||
|
|
6c3af53dfa | ||
|
|
36168b2e33 | ||
|
|
e5072741f0 | ||
|
|
0876535b44 | ||
|
|
38dc42ade8 | ||
|
|
5668f1060b | ||
|
|
80426c5293 | ||
|
|
ac75bb6e3a | ||
|
|
ae7c1f510f | ||
|
|
a2a6448e03 | ||
|
|
f38be4c6d9 | ||
|
|
9065355ee0 | ||
|
|
746fd9e8d2 | ||
|
|
4c63322024 | ||
|
|
881e93e0f6 | ||
|
|
40b163ac39 | ||
|
|
99677b0e21 | ||
|
|
d9f9712487 | ||
|
|
c1620d620e | ||
|
|
cf977a32aa | ||
|
|
54e6786ceb | ||
|
|
a498efd21d | ||
|
|
c0e0499204 | ||
|
|
018e27a22a | ||
|
|
11bba90ded | ||
|
|
1a1ecd1613 | ||
|
|
520b2bab49 | ||
|
|
e7e66f44e2 | ||
|
|
4d886f0343 | ||
|
|
73abd61eba | ||
|
|
b2d5db2702 | ||
|
|
621c3703fd | ||
|
|
f9333158e2 | ||
|
|
c838e9dd7b | ||
|
|
0d3ab09c14 | ||
|
|
bcec740d60 | ||
|
|
602b77e67b | ||
|
|
6205ae81c9 | ||
|
|
b6cabbe3ed | ||
|
|
1cf39d0ef1 | ||
|
|
046c1d8ed2 | ||
|
|
64f2d6899a | ||
|
|
d69c0a5769 | ||
|
|
36a160e1c7 | ||
|
|
4b015c8ced | ||
|
|
15f959b24e | ||
|
|
d13ccc861e | ||
|
|
686b1ddb7a | ||
|
|
b331ef6923 | ||
|
|
328d164c05 | ||
|
|
ac514c3765 | ||
|
|
623a64b610 | ||
|
|
4c899ae2f2 | ||
|
|
5dae396842 | ||
|
|
bc1d5effd3 | ||
|
|
340fa5fd28 | ||
|
|
1143710f8d | ||
|
|
7626331866 | ||
|
|
db8e137c11 | ||
|
|
b9f5819be5 | ||
|
|
ac53526a27 | ||
|
|
930b9d1aa4 | ||
|
|
0bf945cd13 | ||
|
|
c6747074f5 | ||
|
|
f30c250a20 | ||
|
|
41f6c9f516 | ||
|
|
dfd31cdf35 | ||
|
|
099b72be05 | ||
|
|
9155927f89 | ||
|
|
f46d9b21a2 | ||
|
|
456a410946 | ||
|
|
c48bd4f9d6 | ||
|
|
0c6b5a0a93 | ||
|
|
f07a360da2 | ||
|
|
a79d50e6e8 | ||
|
|
7cb64515bd | ||
|
|
e768497cfd | ||
|
|
b9d35cbd4e | ||
|
|
ab2d39407c | ||
|
|
d104222fdd | ||
|
|
5e2d2ddde5 | ||
|
|
6307850721 | ||
|
|
9eec5b64fb | ||
|
|
4a19322ef8 | ||
|
|
9418f28348 | ||
|
|
348f3a16d0 | ||
|
|
9ba58eb05b | ||
|
|
758ea96ea4 | ||
|
|
79c46bbd38 | ||
|
|
571cfd52ae | ||
|
|
f842efb611 | ||
|
|
5afb616205 | ||
|
|
9b7b74524f | ||
|
|
c4f8731262 | ||
|
|
4fcbe9c064 | ||
|
|
d4a8801cd2 | ||
|
|
108eca4e53 | ||
|
|
d4ef97ef5a | ||
|
|
263c202c69 | ||
|
|
3b0b9615ca | ||
|
|
2fcb504b9a | ||
|
|
2e354b435c | ||
|
|
98da0884f1 | ||
|
|
d2f1bc0b8e | ||
|
|
980cabe94f | ||
|
|
bbe65f2a57 | ||
|
|
f6aa100435 | ||
|
|
e74424aaaa | ||
|
|
19eb54df8d | ||
|
|
bfbbd4c589 | ||
|
|
c156ab5e74 | ||
|
|
422df82b7c | ||
|
|
19b7044773 | ||
|
|
f47b3bb87f | ||
|
|
ab7a2db869 | ||
|
|
ede5a8c636 | ||
|
|
e67c0f39a3 | ||
|
|
19b0adf737 | ||
|
|
78abf16f28 | ||
|
|
34eaeca0bb | ||
|
|
e235c111a1 | ||
|
|
a146dce321 | ||
|
|
758284c2b3 | ||
|
|
dae3c1645b | ||
|
|
d8903ed772 | ||
|
|
3f3773dc71 | ||
|
|
95c555d03a | ||
|
|
9c42df3f83 | ||
|
|
fb4bc03da0 | ||
|
|
7242dc2612 | ||
|
|
9ffcee1317 | ||
|
|
d1731a7f15 | ||
|
|
34e6d41554 | ||
|
|
61796d7c42 | ||
|
|
167e2ef382 | ||
|
|
d6de8da3c8 | ||
|
|
0d08ad4179 | ||
|
|
1c5b2b21b0 | ||
|
|
9e76203bc9 | ||
|
|
1d84b112f1 | ||
|
|
b3bff2dd1b | ||
|
|
4b51ad21da | ||
|
|
c87a4c29b4 | ||
|
|
c3c7ea57a2 | ||
|
|
557abecf91 | ||
|
|
5632746eb6 | ||
|
|
270c2922ff | ||
|
|
44c50cf2af | ||
|
|
56f307fd37 | ||
|
|
15d6328ae9 | ||
|
|
cb95b974fc | ||
|
|
93785a6bc2 | ||
|
|
6fa56b2019 | ||
|
|
f29f642e72 | ||
|
|
69ce2c34cf | ||
|
|
5d53addc60 | ||
|
|
009498fae1 | ||
|
|
1d40c72519 | ||
|
|
d08bdb9554 | ||
|
|
d3483ea5c2 | ||
|
|
d0e2e4340e | ||
|
|
24842f3313 | ||
|
|
1cda78412b | ||
|
|
399fa9a157 | ||
|
|
1fd2066946 | ||
|
|
463647ba59 | ||
|
|
cb0f6322f4 | ||
|
|
4fd9a74ae8 | ||
|
|
5490e67ecf | ||
|
|
12905e3df3 | ||
|
|
5d2d6d4a30 | ||
|
|
b40fcf0d67 | ||
|
|
d7758e827d | ||
|
|
3ae2b003aa | ||
|
|
cce7f2c099 | ||
|
|
a4b35b913e | ||
|
|
180037c156 | ||
|
|
71cb1a25db | ||
|
|
839a214cef | ||
|
|
f1323accda | ||
|
|
6b24a0e413 | ||
|
|
18d21eb1c6 | ||
|
|
1370eafac4 | ||
|
|
466977b620 | ||
|
|
2fdbfc651e | ||
|
|
843d125b99 | ||
|
|
a32330ef5b | ||
|
|
6ac3f80887 | ||
|
|
01d7179604 | ||
|
|
789be1174b | ||
|
|
2e2fcd01b6 | ||
|
|
4f017ac1de | ||
|
|
60cef7e938 | ||
|
|
35fad9f47c | ||
|
|
31964e55c1 | ||
|
|
6e2baad3ef | ||
|
|
ebd8bccfb3 | ||
|
|
f90a670fb3 | ||
|
|
df480edb05 | ||
|
|
6da0620169 | ||
|
|
f897363297 | ||
|
|
77c72ca23f | ||
|
|
5379324519 | ||
|
|
f32594d9fd | ||
|
|
3781bd0b66 | ||
|
|
cfbee7781d | ||
|
|
ee025ea49e | ||
|
|
9f7a0c8625 | ||
|
|
7b5123df16 | ||
|
|
d8e43c1f95 | ||
|
|
fa393a1e30 | ||
|
|
581b93c5ea | ||
|
|
a0bfa3261b | ||
|
|
dd737623f4 | ||
|
|
e7376ef269 | ||
|
|
0d93b9b1ed | ||
|
|
1b2847696d | ||
|
|
27e2c51175 | ||
|
|
6982eebfd6 | ||
|
|
7fd11ad174 | ||
|
|
d4253ee4d0 | ||
|
|
3374aba5ab | ||
|
|
e85543719c | ||
|
|
c974628bee | ||
|
|
5c1e0ae0d8 | ||
|
|
8f2d07518f | ||
|
|
0516a946c4 | ||
|
|
9c7628b6cb | ||
|
|
7a6de4053a | ||
|
|
20d6a77275 | ||
|
|
c4a00f19fd | ||
|
|
38cbffc18b | ||
|
|
5a7ab5d4d0 | ||
|
|
3add45dfdf | ||
|
|
e46de87355 | ||
|
|
88bab8bf5e | ||
|
|
cd61ac7f00 | ||
|
|
acd30afe6f | ||
|
|
2271702e6c | ||
|
|
dafa81aef0 | ||
|
|
9f718b7e36 | ||
|
|
4dcbf009da | ||
|
|
e72733d37e | ||
|
|
c28ba7da5e | ||
|
|
62e3d18144 | ||
|
|
7d47ef0b82 | ||
|
|
c9cf4b8997 | ||
|
|
df0be4f37c | ||
|
|
4e4a294d19 |
@@ -0,0 +1,2 @@
|
||||
# Generated by CLion for Google
|
||||
BasedOnStyle: Google
|
||||
+1
-1
@@ -30,7 +30,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${GCC_CLANG_COMMON_FLAGS_RELEASE}")
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
message(STATUS "MSVC detected - Adding compiler flags")
|
||||
# set(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj")
|
||||
set(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj")
|
||||
# set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Od /Zi /D_DEBUG /DDEBUG")
|
||||
# set(CMAKE_CXX_FLAGS_RELEASE "/O1 /DNDEBUG")
|
||||
add_compile_options(/Zc:preprocessor)
|
||||
|
||||
@@ -31,24 +31,28 @@ public:
|
||||
[[nodiscard]] utils::result<utils::version, utils::error> client_version() const override;
|
||||
[[nodiscard]] utils::result<utils::version, utils::error> server_version() const override;
|
||||
|
||||
utils::result<size_t, utils::error> execute(const std::string &stmt) override;
|
||||
utils::result<sql::execute_result, utils::error> execute(const sql::query_context &context) override;
|
||||
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> prepare(const sql::query_context &context) override;
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &context) override;
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &ctx) override;
|
||||
|
||||
utils::result<std::vector<object::attribute_definition>, utils::error> describe(const std::string& table) override;
|
||||
utils::result<std::vector<object::attribute>, utils::error> describe(const std::string& table) override;
|
||||
utils::result<bool, utils::error> exists(const std::string &schema_name, const std::string &table_name) override;
|
||||
|
||||
[[nodiscard]] std::string to_escaped_string( const utils::blob& value ) const override;
|
||||
utils::result<bool, utils::error> sequence_exists(const std::string &schema_name, const std::string &sequence_name) override;
|
||||
|
||||
[[nodiscard]] std::string to_escaped_string( const utils::blob_type_t& value ) const override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ;
|
||||
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &ctx) ;
|
||||
|
||||
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const std::string &stmt) const;
|
||||
|
||||
private:
|
||||
PGconn *conn_{nullptr};
|
||||
|
||||
using string_to_int_map = std::unordered_map<std::string, unsigned long>;
|
||||
using hash_to_string_map = std::unordered_map<size_t, std::string>;
|
||||
|
||||
static string_to_int_map statement_name_map_;
|
||||
static hash_to_string_map statement_name_map_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#ifndef QUERY_POSTGRES_PARAMETER_BINDER_H
|
||||
#define QUERY_POSTGRES_PARAMETER_BINDER_H
|
||||
|
||||
#include "matador/utils/attribute_writer.hpp"
|
||||
#include "matador/sql/interface/parameter_binder.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace matador::backends::postgres {
|
||||
|
||||
class postgres_parameter_binder final : public utils::attribute_writer
|
||||
{
|
||||
class postgres_parameter_binder final : public sql::parameter_binder {
|
||||
public:
|
||||
struct bind_data {
|
||||
explicit bind_data(size_t size);
|
||||
@@ -32,13 +31,14 @@ public:
|
||||
void write_value(size_t pos, const bool &x) override;
|
||||
void write_value(size_t pos, const float &x) override;
|
||||
void write_value(size_t pos, const double &x) override;
|
||||
void write_value(size_t pos, const time &x ) override;
|
||||
void write_value(size_t pos, const date &x ) override;
|
||||
void write_value(size_t pos, const utils::date_type_t &x ) override;
|
||||
void write_value(size_t pos, const utils::time_type_t &x ) override;
|
||||
void write_value(size_t pos, const utils::timestamp_type_t &x ) override;
|
||||
void write_value(size_t pos, const char *x) override;
|
||||
void write_value(size_t pos, const char *x, size_t size) override;
|
||||
void write_value(size_t pos, const std::string &x) override;
|
||||
void write_value(size_t pos, const std::string &x, size_t size) override;
|
||||
void write_value(size_t pos, const utils::blob &x) override;
|
||||
void write_value(size_t pos, const utils::blob_type_t &x) override;
|
||||
void write_value(size_t pos, const utils::value &x, size_t size) override;
|
||||
|
||||
[[nodiscard]] const bind_data& params() const;
|
||||
|
||||
@@ -9,8 +9,7 @@ namespace matador::backends::postgres {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class empty_binder final : public utils::attribute_reader
|
||||
{
|
||||
class empty_binder final : public utils::attribute_reader {
|
||||
public:
|
||||
void read_value(const char *, size_t, int8_t &) override {}
|
||||
void read_value(const char *, size_t, int16_t &) override {}
|
||||
@@ -23,19 +22,19 @@ public:
|
||||
void read_value(const char *, size_t, bool &) override {}
|
||||
void read_value(const char *, size_t, float &) override {}
|
||||
void read_value(const char *, size_t, double &) override {}
|
||||
void read_value(const char *, size_t, time &) override {}
|
||||
void read_value(const char *, size_t, date &) override {}
|
||||
void read_value(const char *, size_t, utils::date_type_t &) override {}
|
||||
void read_value(const char *, size_t, utils::time_type_t &) override {}
|
||||
void read_value(const char *, size_t, utils::timestamp_type_t &) override {}
|
||||
void read_value(const char *, size_t, char *, size_t) override {}
|
||||
void read_value(const char *, size_t, std::string &) override {}
|
||||
void read_value(const char *, size_t, std::string &, size_t) override {}
|
||||
void read_value(const char *, size_t, utils::blob &) override {}
|
||||
void read_value(const char *, size_t, utils::blob_type_t &) override {}
|
||||
void read_value(const char *, size_t, utils::value &, size_t) override {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class postgres_result_reader final : public sql::query_result_reader
|
||||
{
|
||||
class postgres_result_reader final : public sql::query_result_reader {
|
||||
public:
|
||||
explicit postgres_result_reader(PGresult *result);
|
||||
~postgres_result_reader() override;
|
||||
@@ -57,12 +56,13 @@ public:
|
||||
void read_value(const char *id, size_t index, bool &value) override;
|
||||
void read_value(const char *id, size_t index, float &value) override;
|
||||
void read_value(const char *id, size_t index, double &value) override;
|
||||
void read_value(const char *id, size_t index, matador::time &value) override;
|
||||
void read_value(const char *id, size_t index, matador::date &value) override;
|
||||
void read_value(const char *id, size_t index, utils::date_type_t &) override;
|
||||
void read_value(const char *id, size_t index, utils::time_type_t &) override;
|
||||
void read_value(const char *id, size_t index, utils::timestamp_type_t &) override;
|
||||
void read_value(const char *id, size_t index, char *value, size_t size) override;
|
||||
void read_value(const char *id, size_t index, std::string &value) override;
|
||||
void read_value(const char *id, size_t index, std::string &value, size_t size) override;
|
||||
void read_value(const char *id, size_t index, utils::blob &value) override;
|
||||
void read_value(const char *id, size_t index, utils::blob_type_t &value) override;
|
||||
void read_value(const char *id, size_t index, utils::value &val, size_t size) override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#define QUERY_POSTGRES_STATEMENT_HPP
|
||||
|
||||
#include "matador/sql/interface/statement_impl.hpp"
|
||||
|
||||
#include "postgres_parameter_binder.h"
|
||||
#include "matador/sql/interface/parameter_binder.hpp"
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
||||
@@ -12,21 +11,20 @@ namespace matador::backends::postgres {
|
||||
class postgres_statement final : public sql::statement_impl
|
||||
{
|
||||
public:
|
||||
postgres_statement(PGconn *db, PGresult *result, std::string name, const sql::query_context &query);
|
||||
postgres_statement(PGconn *db, PGresult *res, std::string name, const sql::query_context &query);
|
||||
~postgres_statement() override;
|
||||
|
||||
utils::result<sql::execute_result, utils::error> execute(const sql::parameter_binder& bindings) override;
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::parameter_binder& bindings) override;
|
||||
|
||||
utils::result<size_t, utils::error> execute() override;
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch() override;
|
||||
void reset() override;
|
||||
protected:
|
||||
utils::attribute_writer& binder() override;
|
||||
[[nodiscard]] std::unique_ptr<utils::attribute_writer> create_binder() const override;
|
||||
|
||||
private:
|
||||
PGconn *db_{nullptr};
|
||||
PGresult *result_{nullptr};
|
||||
PGresult *res_ = nullptr;
|
||||
|
||||
std::string name_;
|
||||
|
||||
postgres_parameter_binder binder_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,21 +3,22 @@
|
||||
#include "postgres_result_reader.hpp"
|
||||
#include "postgres_statement.hpp"
|
||||
|
||||
#include "matador/object/attribute_definition.hpp"
|
||||
#include "matador/object/attribute.hpp"
|
||||
|
||||
#include "matador/sql/error_code.hpp"
|
||||
#include "matador/sql/record.hpp"
|
||||
|
||||
#include "matador/sql/internal/query_result_impl.hpp"
|
||||
|
||||
#include "matador/utils/string.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
namespace matador::backends::postgres {
|
||||
postgres_connection::string_to_int_map postgres_connection::statement_name_map_{};
|
||||
postgres_connection::hash_to_string_map postgres_connection::statement_name_map_{};
|
||||
|
||||
postgres_connection::postgres_connection(const sql::connection_info &info)
|
||||
: connection_impl(info) {
|
||||
: connection_impl(info) {
|
||||
}
|
||||
|
||||
utils::result<void, utils::error> postgres_connection::open() {
|
||||
@@ -34,7 +35,7 @@ utils::result<void, utils::error> postgres_connection::open() {
|
||||
const std::string msg = PQerrorMessage(conn_);
|
||||
PQfinish(conn_);
|
||||
conn_ = nullptr;
|
||||
return utils::failure(make_error(sql::error_code::OPEN_ERROR, nullptr, conn_, "Failed to connect"));
|
||||
return utils::failure(make_error(sql::error_code::OpenError, nullptr, conn_, "Failed to connect"));
|
||||
}
|
||||
|
||||
return utils::ok<void>();
|
||||
@@ -70,7 +71,7 @@ utils::result<utils::version, utils::error> postgres_connection::server_version(
|
||||
const auto server_version = PQserverVersion(conn_);
|
||||
|
||||
if (server_version == 0) {
|
||||
return utils::failure(make_error(sql::error_code::FAILURE, nullptr, conn_, "Failed to get server version"));
|
||||
return utils::failure(make_error(sql::error_code::Failure, nullptr, conn_, "Failed to get server version"));
|
||||
}
|
||||
|
||||
return utils::ok(utils::version{
|
||||
@@ -82,18 +83,22 @@ utils::result<utils::version, utils::error> postgres_connection::server_version(
|
||||
|
||||
utils::basic_type oid2type(Oid oid);
|
||||
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_connection::fetch(const sql::query_context &context) {
|
||||
PGresult *res = PQexec(conn_, context.sql.c_str());
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_connection::fetch(const sql::query_context &ctx) {
|
||||
PGresult *res = PQexec(conn_, ctx.sql.c_str());
|
||||
|
||||
if (is_result_error(res)) {
|
||||
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Failed to fetch", context.sql));
|
||||
const auto err = make_error(sql::error_code::FetchFailed, res, conn_, "Failed to fetch", ctx.sql);
|
||||
PQclear(res);
|
||||
return utils::failure(err);
|
||||
}
|
||||
|
||||
std::vector<object::attribute_definition> prototype = context.prototype;
|
||||
std::vector<object::attribute> prototype = ctx.prototype;
|
||||
|
||||
const int num_col = PQnfields(res);
|
||||
if (prototype.size() != static_cast<size_t>(num_col)) {
|
||||
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Number of received columns doesn't match expected columns.", context.sql));
|
||||
const auto err = make_error(sql::error_code::FetchFailed, res, conn_, "Number of received columns doesn't match expected columns.", ctx.sql);
|
||||
PQclear(res);
|
||||
return utils::failure(err);
|
||||
}
|
||||
for (int i = 0; i < num_col; ++i) {
|
||||
if (!prototype.at(i).is_null()) {
|
||||
@@ -102,117 +107,145 @@ utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_co
|
||||
const auto type = oid2type(PQftype(res, i));
|
||||
// const char *col_name = PQfname(res, i);
|
||||
// const auto size = PQfmod(res, i);
|
||||
prototype.at(i).type(type);
|
||||
prototype.at(i).change_type(type);
|
||||
}
|
||||
|
||||
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), prototype));
|
||||
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
|
||||
std::move(prototype),
|
||||
ctx.resolver,
|
||||
ctx.result_type));
|
||||
}
|
||||
|
||||
std::string postgres_connection::generate_statement_name(const sql::query_context &query) {
|
||||
std::stringstream name;
|
||||
name << query.table.name << "_" << query.command_name;
|
||||
auto result = postgres_connection::statement_name_map_.find(name.str());
|
||||
std::string postgres_connection::generate_statement_name(const sql::query_context &ctx) {
|
||||
auto it = statement_name_map_.find(ctx.sql_hash);
|
||||
|
||||
if (result == postgres_connection::statement_name_map_.end()) {
|
||||
result = postgres_connection::statement_name_map_.insert(std::make_pair(name.str(), 0)).first;
|
||||
if (it == statement_name_map_.end()) {
|
||||
it = statement_name_map_.insert(std::make_pair(ctx.sql_hash, utils::to_hex_string(ctx.sql_hash))).first;
|
||||
}
|
||||
|
||||
name << "_" << ++result->second;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return name.str();
|
||||
utils::result<sql::execute_result, utils::error> postgres_connection::execute(const std::string& stmt) const {
|
||||
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||
|
||||
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
|
||||
return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to execute", stmt));
|
||||
}
|
||||
|
||||
const size_t affected_rows = utils::to<size_t>(PQcmdTuples(res));
|
||||
|
||||
PQclear(res);
|
||||
|
||||
return utils::ok(sql::execute_result{affected_rows});
|
||||
}
|
||||
|
||||
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_connection::prepare(const sql::query_context &context) {
|
||||
auto statement_name = postgres_connection::generate_statement_name(context);
|
||||
auto statement_name = generate_statement_name(context);
|
||||
|
||||
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(),
|
||||
static_cast<int>(context.bind_vars.size()), nullptr);
|
||||
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast<int>(context.bind_vars.size()), nullptr);
|
||||
|
||||
if (is_result_error(result)) {
|
||||
return utils::failure(make_error(sql::error_code::PREPARE_FAILED, result, conn_, "Failed to prepare", context.sql));
|
||||
return utils::failure(make_error(sql::error_code::PrepareFailed, result, conn_, "Failed to prepare", context.sql));
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::statement_impl> s(std::make_unique<postgres_statement>(conn_, result, statement_name, context));
|
||||
return utils::ok(std::move(s));
|
||||
}
|
||||
|
||||
utils::result<size_t, utils::error> postgres_connection::execute(const std::string &stmt) {
|
||||
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||
utils::result<sql::execute_result, utils::error> postgres_connection::execute(const sql::query_context &context) {
|
||||
return execute(context.sql);
|
||||
PGresult *res = PQexec(conn_, context.sql.c_str());
|
||||
|
||||
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK &&
|
||||
status != PGRES_TUPLES_OK) {
|
||||
return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to execute", stmt));
|
||||
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
|
||||
return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to execute", context.sql));
|
||||
}
|
||||
|
||||
const auto affected_rows = utils::to<size_t>(PQcmdTuples(res));
|
||||
const size_t affected_rows = utils::to<size_t>(PQcmdTuples(res));
|
||||
|
||||
PQclear(res);
|
||||
|
||||
return utils::ok(static_cast<size_t>(affected_rows));
|
||||
return utils::ok(sql::execute_result{affected_rows});
|
||||
}
|
||||
|
||||
utils::basic_type oid2type(const Oid oid) {
|
||||
switch (oid) {
|
||||
case 16:
|
||||
return utils::basic_type::type_bool;
|
||||
return utils::basic_type::Boolean;
|
||||
case 17:
|
||||
return utils::basic_type::type_blob;
|
||||
return utils::basic_type::Blob;
|
||||
case 18:
|
||||
return utils::basic_type::type_int8;
|
||||
return utils::basic_type::Int8;
|
||||
case 21:
|
||||
return utils::basic_type::type_int16;
|
||||
return utils::basic_type::Int16;
|
||||
case 23:
|
||||
return utils::basic_type::type_int32;
|
||||
return utils::basic_type::Int32;
|
||||
case 20:
|
||||
return utils::basic_type::type_int64;
|
||||
return utils::basic_type::Int64;
|
||||
case 25:
|
||||
return utils::basic_type::type_text;
|
||||
return utils::basic_type::Text;
|
||||
case 1043:
|
||||
return utils::basic_type::type_varchar;
|
||||
return utils::basic_type::Varchar;
|
||||
case 700:
|
||||
return utils::basic_type::type_float;
|
||||
return utils::basic_type::Float;
|
||||
case 701:
|
||||
return utils::basic_type::type_double;
|
||||
return utils::basic_type::Double;
|
||||
case 1082:
|
||||
return utils::basic_type::type_date;
|
||||
return utils::basic_type::Date;
|
||||
case 1083:
|
||||
return utils::basic_type::Time;
|
||||
case 1114:
|
||||
return utils::basic_type::type_time;
|
||||
return utils::basic_type::DateTime;
|
||||
default:
|
||||
return utils::basic_type::type_null;
|
||||
return utils::basic_type::Null;
|
||||
}
|
||||
}
|
||||
|
||||
utils::basic_type string2type(const char *type) {
|
||||
if (strcmp(type, "int2") == 0) {
|
||||
return utils::basic_type::type_int16;
|
||||
} else if (strcmp(type, "int4") == 0) {
|
||||
return utils::basic_type::type_int32;
|
||||
} else if (strcmp(type, "int8") == 0) {
|
||||
return utils::basic_type::type_int64;
|
||||
} else if (strcmp(type, "bool") == 0) {
|
||||
return utils::basic_type::type_bool;
|
||||
} else if (strcmp(type, "date") == 0) {
|
||||
return utils::basic_type::type_date;
|
||||
} else if (strcmp(type, "timestamp") == 0) {
|
||||
return utils::basic_type::type_time;
|
||||
} else if (strcmp(type, "float4") == 0) {
|
||||
return utils::basic_type::type_float;
|
||||
} else if (strcmp(type, "float8") == 0) {
|
||||
return utils::basic_type::type_double;
|
||||
} else if (strncmp(type, "varchar", 7) == 0) {
|
||||
return utils::basic_type::type_varchar;
|
||||
} else if (strcmp(type, "character varying") == 0) {
|
||||
return utils::basic_type::type_varchar;
|
||||
} else if (strcmp(type, "text") == 0) {
|
||||
return utils::basic_type::type_text;
|
||||
} else if (strcmp(type, "bytea") == 0) {
|
||||
return utils::basic_type::type_blob;
|
||||
} else {
|
||||
return utils::basic_type::type_null;
|
||||
return utils::basic_type::Int16;
|
||||
}
|
||||
if (strcmp(type, "int4") == 0) {
|
||||
return utils::basic_type::Int32;
|
||||
}
|
||||
if (strcmp(type, "int8") == 0) {
|
||||
return utils::basic_type::Int64;
|
||||
}
|
||||
if (strcmp(type, "bool") == 0) {
|
||||
return utils::basic_type::Boolean;
|
||||
}
|
||||
if (strcmp(type, "date") == 0) {
|
||||
return utils::basic_type::Date;
|
||||
}
|
||||
if (strcmp(type, "time") == 0) {
|
||||
return utils::basic_type::Time;
|
||||
}
|
||||
if (strcmp(type, "timestamp") == 0) {
|
||||
return utils::basic_type::DateTime;
|
||||
}
|
||||
if (strcmp(type, "float4") == 0) {
|
||||
return utils::basic_type::Float;
|
||||
}
|
||||
if (strcmp(type, "float8") == 0) {
|
||||
return utils::basic_type::Double;
|
||||
}
|
||||
if (strncmp(type, "varchar", 7) == 0) {
|
||||
return utils::basic_type::Varchar;
|
||||
}
|
||||
if (strcmp(type, "character varying") == 0) {
|
||||
return utils::basic_type::Varchar;
|
||||
}
|
||||
if (strcmp(type, "text") == 0) {
|
||||
return utils::basic_type::Text;
|
||||
}
|
||||
if (strcmp(type, "bytea") == 0) {
|
||||
return utils::basic_type::Blob;
|
||||
}
|
||||
|
||||
return utils::basic_type::Null;
|
||||
}
|
||||
|
||||
utils::result<std::vector<object::attribute_definition>, utils::error> postgres_connection::describe(const std::string &table) {
|
||||
utils::result<std::vector<object::attribute>, utils::error> postgres_connection::describe(const std::string &table) {
|
||||
const std::string stmt(
|
||||
"SELECT ordinal_position, column_name, udt_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema='public' AND table_name='"
|
||||
+ table + "'");
|
||||
@@ -220,11 +253,11 @@ utils::result<std::vector<object::attribute_definition>, utils::error> postgres_
|
||||
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||
|
||||
if (is_result_error(res)) {
|
||||
return utils::failure(make_error(sql::error_code::DESCRIBE_FAILED, res, conn_, "Failed to describe", stmt));
|
||||
return utils::failure(make_error(sql::error_code::DescribeFailed, res, conn_, "Failed to describe", stmt));
|
||||
}
|
||||
|
||||
postgres_result_reader reader(res);
|
||||
std::vector<object::attribute_definition> prototype;
|
||||
std::vector<object::attribute> prototype;
|
||||
while (auto fetched = reader.fetch()) {
|
||||
if (!fetched.is_ok()) {
|
||||
return utils::failure(fetched.release_error());
|
||||
@@ -234,49 +267,66 @@ utils::result<std::vector<object::attribute_definition>, utils::error> postgres_
|
||||
}
|
||||
char *end = nullptr;
|
||||
// Todo: Handle error
|
||||
auto index = strtoul(reader.column(0), &end, 10) - 1;
|
||||
// auto index = strtoul(reader.column(0), &end, 10) - 1;
|
||||
std::string name = reader.column(1);
|
||||
|
||||
// Todo: extract size
|
||||
auto type = (string2type(reader.column(2)));
|
||||
end = nullptr;
|
||||
object::null_option null_opt{object::null_option::NULLABLE};
|
||||
object::null_option_type null_opt{object::null_option_type::Nullable};
|
||||
if (strtoul(reader.column(4), &end, 10) == 0) {
|
||||
null_opt = object::null_option::NOT_NULL;
|
||||
null_opt = object::null_option_type::NotNull;
|
||||
}
|
||||
// f.default_value(res->column(4));
|
||||
prototype.emplace_back(name, type, utils::null_attributes, null_opt, index);
|
||||
prototype.emplace_back(name, type, NullAttributes, null_opt);
|
||||
}
|
||||
|
||||
return utils::ok(prototype);
|
||||
}
|
||||
|
||||
utils::result<bool, utils::error> postgres_connection::exists(const std::string &schema_name, const std::string &table_name) {
|
||||
const std::string stmt(
|
||||
"SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name
|
||||
+ "'");
|
||||
utils::result<bool, utils::error> postgres_connection::exists(const std::string &schema_name,
|
||||
const std::string &table_name) {
|
||||
std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_name = '" + table_name + "'");
|
||||
if (!schema_name.empty()) {
|
||||
stmt += " AND table_schema = '" + schema_name + "'";
|
||||
}
|
||||
|
||||
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||
|
||||
if (is_result_error(res)) {
|
||||
return utils::failure(make_error(sql::error_code::TABLE_EXISTS_FAILED, res, conn_, "Failed check if table exists", stmt));
|
||||
return utils::failure(make_error(sql::error_code::TableExistsFailed, res, conn_, "Failed check if table exists", stmt));
|
||||
}
|
||||
|
||||
const auto result = utils::to<size_t>(PQcmdTuples(res));
|
||||
if (!result) {
|
||||
return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to convert result value", stmt));
|
||||
PQclear(res);
|
||||
return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to convert result value", stmt));
|
||||
}
|
||||
|
||||
PQclear(res);
|
||||
return utils::ok(*result == 1);
|
||||
}
|
||||
|
||||
std::string postgres_connection::to_escaped_string(const utils::blob& value) const
|
||||
{
|
||||
utils::result<bool, utils::error> postgres_connection::sequence_exists(const std::string& schema_name, const std::string& sequence_name) {
|
||||
const std::string sql{"SELECT to_regclass('" + schema_name + "." + sequence_name + "')"};
|
||||
PGresult* res = PQexec(conn_, sql.c_str());
|
||||
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||||
return utils::failure(make_error(sql::error_code::SequenceExistsFailed, res, conn_, "Failed check if sequence exists", sql));
|
||||
}
|
||||
|
||||
const bool exists = PQgetisnull(res, 0, 0) ? false : true;
|
||||
PQclear(res);
|
||||
|
||||
return utils::ok(exists);
|
||||
}
|
||||
|
||||
std::string postgres_connection::to_escaped_string(const utils::blob_type_t &value) const {
|
||||
size_t escapedDataLength;
|
||||
unsigned char *escapedData = PQescapeByteaConn(conn_, value.data(), value.size(), &escapedDataLength);
|
||||
|
||||
return {reinterpret_cast<char*>(escapedData), escapedDataLength-1};
|
||||
return {reinterpret_cast<char *>(escapedData), escapedDataLength - 1};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
#include "matador/utils/basic_types.hpp"
|
||||
|
||||
[[maybe_unused]] const matador::sql::dialect *get_dialect()
|
||||
{
|
||||
[[maybe_unused]] const matador::sql::dialect *get_dialect() {
|
||||
using namespace matador::sql;
|
||||
const static dialect d = dialect_builder::builder()
|
||||
.create()
|
||||
@@ -13,15 +12,17 @@
|
||||
return "$" + std::to_string(index);
|
||||
})
|
||||
.with_token_replace_map({
|
||||
{dialect_token::BEGIN_BINARY_DATA, "'\\x"}
|
||||
{dialect_token::BeginBinaryData, "'\\x"},
|
||||
{dialect_token::Identity, "GENERATED BY DEFAULT AS IDENTITY"}
|
||||
})
|
||||
.with_data_type_replace_map({
|
||||
{matador::utils::basic_type::type_int8, "SMALLINT"},
|
||||
{matador::utils::basic_type::type_uint8, "SMALLINT"},
|
||||
{matador::utils::basic_type::type_float, "REAL"},
|
||||
{matador::utils::basic_type::type_double, "DOUBLE PRECISION"},
|
||||
{matador::utils::basic_type::type_time, "TIMESTAMP"},
|
||||
{matador::utils::basic_type::type_blob, "BYTEA"}
|
||||
{matador::utils::basic_type::Int8, "SMALLINT"},
|
||||
{matador::utils::basic_type::UInt8, "SMALLINT"},
|
||||
{matador::utils::basic_type::Float, "REAL"},
|
||||
{matador::utils::basic_type::Double, "DOUBLE PRECISION"},
|
||||
{matador::utils::basic_type::Time, "TIME(6)"},
|
||||
{matador::utils::basic_type::DateTime, "TIMESTAMPTZ(6)"},
|
||||
{matador::utils::basic_type::Blob, "BYTEA"}
|
||||
})
|
||||
.with_bool_strings("TRUE", "FALSE")
|
||||
.with_default_schema_name("public")
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
namespace matador::backends::postgres {
|
||||
|
||||
utils::error make_error(const sql::error_code ec, const PGresult *res, const PGconn *db, const std::string &msg,
|
||||
utils::error make_error(const sql::error_code ec,
|
||||
const PGresult *res,
|
||||
const PGconn *db,
|
||||
const std::string &msg,
|
||||
const std::string &sql) {
|
||||
utils::error err(ec, msg);
|
||||
err.add_error_info("dbms", "postgres");
|
||||
@@ -30,22 +33,19 @@ bool is_result_error(const PGresult *res) {
|
||||
return status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK;
|
||||
}
|
||||
|
||||
void throw_postgres_error(const char *what, const std::string &source)
|
||||
{
|
||||
void throw_postgres_error(const char *what, const std::string &source) {
|
||||
std::stringstream msg;
|
||||
msg << "postgres error (" << source << "): " << what;
|
||||
throw std::logic_error(msg.str());
|
||||
}
|
||||
|
||||
void throw_postgres_error(PGconn *db, const std::string &source)
|
||||
{
|
||||
void throw_postgres_error(const PGconn *db, const std::string &source) {
|
||||
if (PQstatus(db) == CONNECTION_BAD) {
|
||||
throw_postgres_error(PQerrorMessage(db), source);
|
||||
}
|
||||
}
|
||||
|
||||
void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql)
|
||||
{
|
||||
void throw_postgres_error(const PGresult *res, const PGconn *db, const std::string &source, const std::string &sql) {
|
||||
if (res == nullptr) {
|
||||
std::stringstream msg;
|
||||
msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql;
|
||||
|
||||
@@ -47,15 +47,15 @@ void bind_value(postgres_parameter_binder::bind_data &data, const size_t index,
|
||||
|
||||
postgres_parameter_binder::bind_data::bind_data(const size_t size)
|
||||
: strings(size)
|
||||
, bytes(size)
|
||||
, values(size)
|
||||
, lengths(size)
|
||||
, formats(size)
|
||||
{}
|
||||
, bytes(size)
|
||||
, values(size)
|
||||
, lengths(size)
|
||||
, formats(size) {
|
||||
}
|
||||
|
||||
postgres_parameter_binder::postgres_parameter_binder(size_t size)
|
||||
: bind_data_(size)
|
||||
{}
|
||||
postgres_parameter_binder::postgres_parameter_binder(const size_t size)
|
||||
: bind_data_(size) {
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::write_value(const size_t pos, const int8_t &x) {
|
||||
detail::bind_value(bind_data_, pos, x);
|
||||
@@ -123,31 +123,42 @@ void postgres_parameter_binder::write_value(const size_t pos, const std::string
|
||||
write_value(pos, x);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::write_value(const size_t pos, const time &/*x*/) {
|
||||
// bind_data_.strings[pos] = utils::to_string(x, "%Y-%m-%d %T.%f");
|
||||
void postgres_parameter_binder::write_value(const size_t pos, const utils::date_type_t &x) {
|
||||
char buf[11]; // "YYYY-MM-DD" + '\0'
|
||||
|
||||
std::snprintf(buf, sizeof(buf), "%04d-%02u-%02u", x.year, static_cast<unsigned>(x.month), static_cast<unsigned>(x.day));
|
||||
bind_data_.strings[pos] = std::string{buf};
|
||||
bind_data_.values[pos] = bind_data_.strings[pos].data();
|
||||
bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size());
|
||||
bind_data_.formats[pos] = 0;
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::write_value(const size_t pos, const date &/*x*/) {
|
||||
// bind_data_.strings[pos] = utils::to_string(x, utils::date_format::ISO8601);
|
||||
void postgres_parameter_binder::write_value(const size_t pos, const utils::time_type_t &x) {
|
||||
char buf[16]; // "HH:MM:SS.ffffff" + '\0'
|
||||
|
||||
std::snprintf(buf, sizeof(buf), "%02u:%02u:%02u.%06u",
|
||||
static_cast<unsigned>(x.hour), static_cast<unsigned>(x.minute), static_cast<unsigned>(x.second), x.microsecond);
|
||||
|
||||
bind_data_.strings[pos] = std::string{buf};
|
||||
bind_data_.values[pos] = bind_data_.strings[pos].data();
|
||||
bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size());
|
||||
bind_data_.formats[pos] = 0;
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::write_value(const size_t pos, const utils::blob &x) {
|
||||
void postgres_parameter_binder::write_value(size_t pos, const utils::timestamp_type_t &x) {
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::write_value(const size_t pos, const utils::blob_type_t &x) {
|
||||
bind_data_.bytes[pos] = x;
|
||||
bind_data_.values[pos] = reinterpret_cast<char*>(bind_data_.bytes[pos].data());
|
||||
bind_data_.values[pos] = reinterpret_cast<char *>(bind_data_.bytes[pos].data());
|
||||
bind_data_.lengths[pos] = static_cast<int>(bind_data_.bytes[pos].size());
|
||||
bind_data_.formats[pos] = 1;
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::write_value(const size_t /*pos*/, const utils::value &/*x*/, size_t /*size*/) {}
|
||||
void postgres_parameter_binder::write_value(const size_t /*pos*/, const utils::value &/*x*/, size_t /*size*/) {
|
||||
}
|
||||
|
||||
const postgres_parameter_binder::bind_data &postgres_parameter_binder::params() const {
|
||||
return bind_data_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,29 +2,26 @@
|
||||
|
||||
#include "matador/utils/convert.hpp"
|
||||
#include "matador/utils/value.hpp"
|
||||
#include "matador/utils/string.hpp"
|
||||
|
||||
namespace matador::backends::postgres {
|
||||
|
||||
postgres_result_reader::postgres_result_reader(PGresult *result)
|
||||
: result_(result)
|
||||
, row_count_(PQntuples(result_))
|
||||
, column_count_(PQnfields(result_))
|
||||
{}
|
||||
, row_count_(PQntuples(result_))
|
||||
, column_count_(PQnfields(result_)) {
|
||||
}
|
||||
|
||||
postgres_result_reader::~postgres_result_reader()
|
||||
{
|
||||
postgres_result_reader::~postgres_result_reader() {
|
||||
if (result_) {
|
||||
PQclear(result_);
|
||||
}
|
||||
}
|
||||
|
||||
size_t postgres_result_reader::column_count() const
|
||||
{
|
||||
size_t postgres_result_reader::column_count() const {
|
||||
return column_count_;
|
||||
}
|
||||
|
||||
const char *postgres_result_reader::column( const size_t index) const
|
||||
{
|
||||
const char *postgres_result_reader::column(const size_t index) const {
|
||||
return PQgetvalue(result_, static_cast<int>(row_index_), static_cast<int>(index));
|
||||
}
|
||||
|
||||
@@ -106,16 +103,28 @@ void postgres_result_reader::read_value(const char * /*id*/, const size_t index,
|
||||
}
|
||||
}
|
||||
|
||||
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, time &/*value*/) {
|
||||
// if (const auto val = column(index); strlen(val) > 0) {
|
||||
// value = time::parse(val, "%Y-%m-%d %T.%f");
|
||||
// }
|
||||
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::date_type_t &value) {
|
||||
if (const auto val = column(index); strlen(val) > 0) {
|
||||
if (const auto res = utils::to<utils::date_type_t>(std::string(val)); res.is_ok()) {
|
||||
value = res.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, date &/*value*/) {
|
||||
// if (const auto val = column(index); strlen(val) > 0) {
|
||||
// value.set(val, matador::utils::date_format::ISO8601);
|
||||
// }
|
||||
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::time_type_t &value) {
|
||||
if (const auto val = column(index); strlen(val) > 0) {
|
||||
if (const auto res = utils::to<utils::time_type_t>(std::string(val)); res.is_ok()) {
|
||||
value = res.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::timestamp_type_t &value) {
|
||||
if (const auto val = column(index); strlen(val) > 0) {
|
||||
if (const auto res = utils::to<utils::timestamp_type_t>(std::string(val)); res.is_ok()) {
|
||||
value = res.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, char *value, size_t size) {
|
||||
@@ -126,7 +135,7 @@ void postgres_result_reader::read_value(const char * /*id*/, const size_t index,
|
||||
#else
|
||||
strncpy(value, val, size);
|
||||
#endif
|
||||
value[size-1] = '\n';
|
||||
value[size - 1] = '\n';
|
||||
} else {
|
||||
#ifdef _MSC_VER
|
||||
strcpy_s(value, size, val);
|
||||
@@ -144,69 +153,71 @@ void postgres_result_reader::read_value(const char * /*id*/, const size_t index,
|
||||
value.assign(column(index));
|
||||
}
|
||||
|
||||
void postgres_result_reader::read_value( const char* id, const size_t index, utils::blob& value )
|
||||
{
|
||||
const auto *data = reinterpret_cast<const unsigned char*>(column(index));
|
||||
// auto length = PQgetlength(result_, row_index_, static_cast<int>(index));
|
||||
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::blob_type_t &value) {
|
||||
const auto *data = reinterpret_cast<const unsigned char *>(column(index));
|
||||
|
||||
size_t length;
|
||||
unsigned char* unescaped = PQunescapeBytea(data, &length);
|
||||
unsigned char *unescaped = PQunescapeBytea(data, &length);
|
||||
|
||||
value.assign(unescaped, unescaped+length);
|
||||
value.assign(unescaped, unescaped + length);
|
||||
|
||||
PQfreemem(unescaped);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void set_value(const char* str, utils::value& value) {
|
||||
template<typename Type>
|
||||
void set_value(const char *str, utils::value &value) {
|
||||
if (const auto res = utils::to<Type>(str); res.is_ok()) {
|
||||
value = res.value();
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void set_value<utils::blob>(const char* str, utils::value& value) {
|
||||
template<>
|
||||
void set_value<utils::blob_type_t>(const char *str, utils::value &value) {
|
||||
size_t length;
|
||||
unsigned char* unescaped = PQunescapeBytea(reinterpret_cast<const unsigned char*>(str), &length);
|
||||
unsigned char *unescaped = PQunescapeBytea(reinterpret_cast<const unsigned char *>(str), &length);
|
||||
|
||||
value = utils::blob(unescaped, unescaped+length);
|
||||
value = utils::blob_type_t(unescaped, unescaped + length);
|
||||
|
||||
PQfreemem(unescaped);
|
||||
}
|
||||
|
||||
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::value &val, size_t) {
|
||||
switch (val.type()) {
|
||||
case utils::basic_type::type_int8:
|
||||
case utils::basic_type::Int8:
|
||||
set_value<int8_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_int16:
|
||||
case utils::basic_type::Int16:
|
||||
set_value<int16_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_int32:
|
||||
case utils::basic_type::Int32:
|
||||
set_value<int32_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_int64:
|
||||
case utils::basic_type::Int64:
|
||||
set_value<int64_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_uint8:
|
||||
case utils::basic_type::UInt8:
|
||||
set_value<uint8_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_uint16:
|
||||
case utils::basic_type::UInt16:
|
||||
set_value<uint16_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_uint32:
|
||||
case utils::basic_type::UInt32:
|
||||
set_value<uint32_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_uint64:
|
||||
case utils::basic_type::UInt64:
|
||||
set_value<uint64_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_float:
|
||||
case utils::basic_type::Float:
|
||||
set_value<float>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_double:
|
||||
case utils::basic_type::Double:
|
||||
set_value<double>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_bool:
|
||||
case utils::basic_type::Boolean:
|
||||
set_value<bool>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::type_text:
|
||||
case utils::basic_type::type_varchar: {
|
||||
case utils::basic_type::Text:
|
||||
case utils::basic_type::Varchar: {
|
||||
if (const auto *column_value = column(index); column_value == nullptr) {
|
||||
val = std::string{};
|
||||
} else {
|
||||
@@ -214,24 +225,30 @@ void postgres_result_reader::read_value(const char * /*id*/, const size_t index,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case utils::basic_type::type_time:
|
||||
case utils::basic_type::type_date: {
|
||||
val = std::string{column(index)};
|
||||
case utils::basic_type::Time:
|
||||
set_value<utils::time_type_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::Date:
|
||||
set_value<utils::date_type_t>(column(index), val);
|
||||
break;
|
||||
case utils::basic_type::DateTime: {
|
||||
set_value<utils::timestamp_type_t>(column(index), val);
|
||||
break;
|
||||
}
|
||||
case utils::basic_type::type_null: {
|
||||
case utils::basic_type::Null: {
|
||||
val = nullptr_t{};
|
||||
break;
|
||||
}
|
||||
case utils::basic_type::type_blob: {
|
||||
set_value<utils::blob>(column(index), val);
|
||||
case utils::basic_type::Blob: {
|
||||
set_value<utils::blob_type_t>(column(index), val);
|
||||
break;
|
||||
}
|
||||
case utils::basic_type::Unknown:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
utils::attribute_reader &postgres_result_reader::result_binder() {
|
||||
return empty_binder_;
|
||||
}
|
||||
|
||||
} // namespace matador::backends::postgres
|
||||
|
||||
@@ -1,61 +1,79 @@
|
||||
#include "postgres_statement.hpp"
|
||||
|
||||
#include "matador/sql/execute_result.hpp"
|
||||
#include "postgres_error.hpp"
|
||||
#include "postgres_parameter_binder.h"
|
||||
#include "postgres_result_reader.hpp"
|
||||
|
||||
namespace matador::backends::postgres {
|
||||
|
||||
postgres_statement::postgres_statement(PGconn *db, PGresult *result, std::string name, const sql::query_context &query)
|
||||
: statement_impl(query)
|
||||
postgres_statement::postgres_statement(PGconn *db, PGresult *res, std::string name, const sql::query_context &query)
|
||||
: statement_impl(query, 0)
|
||||
, db_(db)
|
||||
, result_(result)
|
||||
, res_(res)
|
||||
, name_(std::move(name))
|
||||
, binder_(query_.bind_vars.size())
|
||||
{}
|
||||
|
||||
utils::result<size_t, utils::error> postgres_statement::execute()
|
||||
{
|
||||
postgres_statement::~postgres_statement() {
|
||||
PQclear(res_);
|
||||
}
|
||||
|
||||
utils::result<sql::execute_result, utils::error> postgres_statement::execute(const sql::parameter_binder& bindings) {
|
||||
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
||||
if (!postgres_bindings) {
|
||||
return utils::failure(utils::error(sql::error_code::ExecuteFailed, "Failed to cast bindings to postgres bindings"));
|
||||
}
|
||||
|
||||
if (query_.command == sql::sql_command::Insert) {
|
||||
// handle insert command with the primary key generator strategy
|
||||
}
|
||||
|
||||
PGresult *res = PQexecPrepared(db_,
|
||||
name_.c_str(),
|
||||
static_cast<int>(binder_.params().values.size()),
|
||||
binder_.params().values.data(),
|
||||
binder_.params().lengths.data(),
|
||||
binder_.params().formats.data(),
|
||||
static_cast<int>(postgres_bindings->params().values.size()),
|
||||
postgres_bindings->params().values.data(),
|
||||
postgres_bindings->params().lengths.data(),
|
||||
postgres_bindings->params().formats.data(),
|
||||
0);
|
||||
|
||||
if (is_result_error(res)) {
|
||||
return utils::failure(make_error(sql::error_code::EXECUTE_FAILED, res, db_, "Failed to execute statement", query_.sql));
|
||||
return utils::failure(make_error(sql::error_code::ExecuteFailed, res, db_, "Failed to execute statement", query_.sql));
|
||||
}
|
||||
|
||||
const auto *tuples = PQcmdTuples(res);
|
||||
if (strlen(tuples) == 0) {
|
||||
return utils::ok(static_cast<size_t>(0));
|
||||
size_t value{0};
|
||||
if (const auto *tuples = PQcmdTuples(res); strlen(tuples) != 0) {
|
||||
value = std::stoul(tuples);
|
||||
}
|
||||
|
||||
return utils::ok(static_cast<size_t>(std::stoul(tuples)));
|
||||
PQclear(res);
|
||||
|
||||
return utils::ok(sql::execute_result{value});
|
||||
}
|
||||
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch()
|
||||
{
|
||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch(const sql::parameter_binder& bindings) {
|
||||
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
||||
if (!postgres_bindings) {
|
||||
return utils::failure(utils::error(sql::error_code::ExecuteFailed, "Failed to cast bindings to postgres bindings"));
|
||||
}
|
||||
PGresult *res = PQexecPrepared(db_,
|
||||
name_.c_str(),
|
||||
static_cast<int>(binder_.params().values.size()),
|
||||
binder_.params().values.data(),
|
||||
binder_.params().lengths.data(),
|
||||
binder_.params().formats.data(),
|
||||
static_cast<int>(postgres_bindings->params().values.size()),
|
||||
postgres_bindings->params().values.data(),
|
||||
postgres_bindings->params().lengths.data(),
|
||||
postgres_bindings->params().formats.data(),
|
||||
0);
|
||||
|
||||
if (is_result_error(res)) {
|
||||
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql));
|
||||
return utils::failure(make_error(sql::error_code::FetchFailed, res, db_, "Failed to fetch statement", query_.sql));
|
||||
}
|
||||
|
||||
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), query_.prototype));
|
||||
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
|
||||
query_.prototype,
|
||||
query_.resolver,
|
||||
query_.result_type));
|
||||
}
|
||||
|
||||
void postgres_statement::reset() {}
|
||||
|
||||
utils::attribute_writer& postgres_statement::binder()
|
||||
{
|
||||
return binder_;
|
||||
std::unique_ptr<utils::attribute_writer> postgres_statement::create_binder() const {
|
||||
return std::make_unique<postgres_parameter_binder>(query_.bind_vars.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
CPMAddPackage("gh:catchorg/Catch2@3.7.1")
|
||||
CPMAddPackage("gh:catchorg/Catch2@3.14.0")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
||||
|
||||
#set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:15442/matador")
|
||||
set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:5432/matador")
|
||||
|
||||
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)
|
||||
@@ -9,9 +10,6 @@ configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/co
|
||||
message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}")
|
||||
|
||||
set(TEST_SOURCES
|
||||
../../../test/models/coordinate.hpp
|
||||
../../../test/models/location.hpp
|
||||
../../../test/models/types.hpp
|
||||
../../../test/backends/ColorEnumTraits.cpp
|
||||
../../../test/backends/ColorEnumTraits.hpp
|
||||
../../../test/backends/ConnectionTest.cpp
|
||||
@@ -21,12 +19,35 @@ set(TEST_SOURCES
|
||||
../../../test/backends/QueryRecordTest.cpp
|
||||
../../../test/backends/QueryStatementTests.cpp
|
||||
../../../test/backends/QueryTest.cpp
|
||||
../../../test/backends/SchemaFixture.cpp
|
||||
../../../test/backends/SchemaFixture.hpp
|
||||
../../../test/backends/SchemaTest.cpp
|
||||
../../../test/backends/SequenceFixture.cpp
|
||||
../../../test/backends/SequenceFixture.hpp
|
||||
../../../test/backends/SequenceTest.cpp
|
||||
../../../test/backends/SessionDeleteHasMany.cpp
|
||||
../../../test/backends/SessionDeleteHasManyToMany.cpp
|
||||
../../../test/backends/SessionFixture.cpp
|
||||
../../../test/backends/SessionFixture.hpp
|
||||
../../../test/backends/SessionInsertBelongsTo.cpp
|
||||
../../../test/backends/SessionInsertHasMany.cpp
|
||||
../../../test/backends/SessionInsertHasManyToManyTest.cpp
|
||||
../../../test/backends/SessionInsertHasOne.cpp
|
||||
../../../test/backends/SessionTest.cpp
|
||||
../../../test/backends/StatementCacheTest.cpp
|
||||
../../../test/backends/StatementTest.cpp
|
||||
../../../test/backends/TableSequenceFixture.cpp
|
||||
../../../test/backends/TableSequenceFixture.hpp
|
||||
../../../test/backends/TableSequenceTest.cpp
|
||||
../../../test/backends/TypeTraitsTest.cpp
|
||||
../../../test/models/author.hpp
|
||||
../../../test/models/coordinate.hpp
|
||||
../../../test/models/location.hpp
|
||||
../../../test/models/model_metas.hpp
|
||||
../../../test/models/types.hpp
|
||||
../../../test/models/user.hpp
|
||||
../../../test/utils/RecordPrinter.cpp
|
||||
../../../test/utils/RecordPrinter.hpp
|
||||
)
|
||||
|
||||
set(LIBRARY_TEST_TARGET PostgresTests)
|
||||
|
||||
+26
-2
@@ -1,9 +1,33 @@
|
||||
add_executable(demo main.cpp
|
||||
../include/matador/query/query_column.hpp)
|
||||
|
||||
|
||||
add_executable(demo main.cpp)
|
||||
target_link_libraries(demo PRIVATE
|
||||
matador-core
|
||||
matador-orm
|
||||
${CMAKE_DL_LIBS}
|
||||
${SQLite3_LIBRARIES}
|
||||
)
|
||||
|
||||
add_executable(sandbox sandbox.cpp)
|
||||
target_link_libraries(sandbox PRIVATE
|
||||
matador-core
|
||||
matador-orm
|
||||
${CMAKE_DL_LIBS}
|
||||
${SQLite3_LIBRARIES}
|
||||
)
|
||||
|
||||
add_executable(work work.cpp)
|
||||
target_link_libraries(work PRIVATE
|
||||
matador-core
|
||||
matador-orm
|
||||
${CMAKE_DL_LIBS}
|
||||
${PostgreSQL_LIBRARY}
|
||||
)
|
||||
|
||||
add_dependencies(work matador-postgres)
|
||||
|
||||
add_executable(object object.cpp)
|
||||
target_link_libraries(object PRIVATE
|
||||
matador-core
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef AUTHOR_HPP
|
||||
#define AUTHOR_HPP
|
||||
|
||||
#include "matador/object/collection.hpp"
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
|
||||
namespace demo {
|
||||
struct book;
|
||||
|
||||
struct author {
|
||||
unsigned int id{};
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
std::string date_of_birth;
|
||||
unsigned short year_of_birth{};
|
||||
bool distinguished{false};
|
||||
matador::object::collection<matador::object::object_ptr<book>> books;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::attribute( op, "first_name", first_name, VarChar63 );
|
||||
field::attribute( op, "last_name", last_name, VarChar63 );
|
||||
field::attribute( op, "date_of_birth", date_of_birth, VarChar63 );
|
||||
field::attribute( op, "year_of_birth", year_of_birth );
|
||||
field::attribute( op, "distinguished", distinguished );
|
||||
field::has_many( op, "books", books, "author_id", utils::CascadeNoneFetchLazy );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //AUTHOR_HPP
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef BOOK_HPP
|
||||
#define BOOK_HPP
|
||||
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
#include "matador/utils/access.hpp"
|
||||
|
||||
namespace demo {
|
||||
struct author;
|
||||
|
||||
struct book {
|
||||
unsigned int id{};
|
||||
matador::object::object_ptr<author> book_author;
|
||||
std::string title;
|
||||
unsigned short published_in{};
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::attribute( op, "title", title, VarChar511 );
|
||||
field::belongs_to( op, "author_id", book_author, utils::CascadeNoneFetchLazy );
|
||||
field::attribute( op, "published_in", published_in );
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif //BOOK_HPP
|
||||
+133
-110
@@ -1,21 +1,20 @@
|
||||
#include "matador/sql/column.hpp"
|
||||
#include "matador/sql/connection.hpp"
|
||||
#include "matador/sql/query_macro.hpp"
|
||||
#include "matador/sql/connection_pool.hpp"
|
||||
|
||||
#include "matador/query/condition.hpp"
|
||||
#include "matador/query/criteria.hpp"
|
||||
#include "matador/query/query.hpp"
|
||||
#include "matador/query/table_column.hpp"
|
||||
#include "matador/query/meta_table_macro.hpp"
|
||||
#include "matador/query/schema.hpp"
|
||||
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
#include "matador/object/schema.hpp"
|
||||
#include "matador/object/repository.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/default_type_traits.hpp"
|
||||
#include "matador/utils/enum_mapper.hpp"
|
||||
#include "matador/utils/types.hpp"
|
||||
|
||||
#include "matador/sql/query_macro.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
struct author {
|
||||
unsigned int id{};
|
||||
@@ -26,14 +25,14 @@ struct author {
|
||||
bool distinguished{false};
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::attribute( op, "first_name", first_name, 63 );
|
||||
field::attribute( op, "last_name", last_name, 63 );
|
||||
field::attribute( op, "date_of_birth", date_of_birth, 31 );
|
||||
field::attribute( op, "year_of_birth", year_of_birth );
|
||||
field::attribute( op, "distinguished", distinguished );
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "first_name", first_name, 63);
|
||||
field::attribute(op, "last_name", last_name, 63);
|
||||
field::attribute(op, "date_of_birth", date_of_birth, 31);
|
||||
field::attribute(op, "year_of_birth", year_of_birth);
|
||||
field::attribute(op, "distinguished", distinguished);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,12 +43,12 @@ struct book {
|
||||
unsigned short published_in{};
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::attribute( op, "title", title, 511 );
|
||||
field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes );
|
||||
field::attribute( op, "published_in", published_in );
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "title", title, 511);
|
||||
field::belongs_to(op, "author_id", book_author, matador::utils::CascadeNoneFetchLazy);
|
||||
field::attribute(op, "published_in", published_in);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,9 +56,9 @@ struct payload {
|
||||
unsigned int id{};
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::primary_key(op, "id", id);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -85,14 +84,14 @@ struct job {
|
||||
job_mode mode;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::belongs_to( op, "payload", data, matador::utils::default_foreign_attributes );
|
||||
field::attribute( op, "type", type, 511 );
|
||||
field::attribute( op, "description", description, 511 );
|
||||
field::attribute( op, "state", state );
|
||||
field::attribute( op, "mode", mode );
|
||||
field::primary_key(op, "id", id);
|
||||
field::belongs_to(op, "payload", data, matador::utils::CascadeNoneFetchLazy);
|
||||
field::attribute(op, "type", type, 511);
|
||||
field::attribute(op, "description", description, 511);
|
||||
field::attribute(op, "state", state);
|
||||
field::attribute(op, "mode", mode);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -102,12 +101,12 @@ static const matador::utils::enum_mapper<job::job_state> job_state_enum({
|
||||
{job::job_state::Succeeded, "Succeeded"},
|
||||
{job::job_state::Failed, "Failed"},
|
||||
{job::job_state::Canceled, "Canceled"}
|
||||
});
|
||||
});
|
||||
|
||||
static const matador::utils::enum_mapper<job::job_mode> job_mode_enum({
|
||||
{job::job_mode::Foreground, "Foreground"},
|
||||
{job::job_mode::Background, "Background"}
|
||||
});
|
||||
});
|
||||
|
||||
template<>
|
||||
struct matador::utils::data_type_traits<job::job_state, void> {
|
||||
@@ -128,7 +127,7 @@ struct matador::utils::data_type_traits<job::job_state, void> {
|
||||
|
||||
template<>
|
||||
struct matador::utils::data_type_traits<job::job_mode, void> {
|
||||
static basic_type type( const std::size_t size) { return data_type_traits<std::string>::type(size); }
|
||||
static basic_type type(const std::size_t size) { return data_type_traits<std::string>::type(size); }
|
||||
|
||||
static void read_value(attribute_reader &reader, const char *id, const size_t index, job::job_mode &value) {
|
||||
std::string enum_string;
|
||||
@@ -143,67 +142,78 @@ struct matador::utils::data_type_traits<job::job_mode, void> {
|
||||
}
|
||||
};
|
||||
|
||||
QUERY_HELPER( authors, id, first_name, last_name, date_of_birth, year_of_birth, distinguished )
|
||||
META_TABLE(authors, AUTHOR, id, first_name, last_name, date_of_birth, year_of_birth, distinguished)
|
||||
|
||||
QUERY_HELPER( books, id, author_id, title, published_in )
|
||||
META_TABLE(books, BOOK, id, author_id, title, published_in)
|
||||
|
||||
QUERY_HELPER( job, id, payload, type, description, state, mode )
|
||||
META_TABLE(job, JOB, id, payload, type, description, state, mode)
|
||||
|
||||
QUERY_HELPER( payload, id )
|
||||
META_TABLE(payload, PAYLOAD, id)
|
||||
|
||||
QUERY_HELPER( temporary_table, id );
|
||||
META_TABLE(temporary_table, TEMPORARY_TABLE, id);
|
||||
|
||||
META_TABLE(customer, CUSTOMER, id, name, email, address)
|
||||
|
||||
META_TABLE(product, PRODUCT, id, title, description, price, category)
|
||||
|
||||
META_TABLE(category, CATEGORY, id, title, description)
|
||||
|
||||
META_TABLE(cart, CART, id, items, owner)
|
||||
|
||||
int main() {
|
||||
using namespace matador::sql;
|
||||
using namespace matador::object;
|
||||
using namespace matador::utils;
|
||||
using namespace matador::query;
|
||||
using namespace matador::query::meta;
|
||||
|
||||
const std::string env_var{"MATADOR_BACKENDS_PATH"};
|
||||
|
||||
std::string dns{"sqlite://demo.db"};
|
||||
schema s( "main" );
|
||||
auto result = s.attach<author>( "authors" ).and_then( [&s] {
|
||||
return s.attach<book>( "books" );
|
||||
} );
|
||||
// s.attach<book>( "books" );
|
||||
connection_pool pool("postgres://test:test123!@127.0.0.1:5432/matador", 4);
|
||||
|
||||
connection c( dns );
|
||||
result = c.open();
|
||||
// s.create( c );
|
||||
//
|
||||
// auto create_authors_sql = c.query( s )
|
||||
// .create()
|
||||
// .table<author>( qh::authors )
|
||||
// .execute();
|
||||
//
|
||||
// c.query( s )
|
||||
// .create()
|
||||
// .table<book>( qh::books )
|
||||
// .execute();
|
||||
//
|
||||
// std::cout << "SQL: " << create_authors_sql << "\n";
|
||||
//
|
||||
// author mc;
|
||||
// mc.id = 1;
|
||||
// mc.first_name = "Michael";
|
||||
// mc.last_name = "Crichton";
|
||||
// mc.date_of_birth = "19.8.1954";
|
||||
// mc.year_of_birth = 1954;
|
||||
// mc.distinguished = true;
|
||||
// auto insert_authors_sql = c.query( s )
|
||||
// .insert()
|
||||
// .into( qh::authors )
|
||||
// .values( mc )
|
||||
// .execute();
|
||||
//
|
||||
// std::cout << "SQL: " << insert_authors_sql << "\n";
|
||||
//
|
||||
// auto result = c.query( s )
|
||||
// .select( qh::authors.columns )
|
||||
// .from( qh::authors )
|
||||
// .fetch_all();
|
||||
//
|
||||
// for (const auto& row: result) { std::cout << "Author " << row.at( qh::authors.first_name ) << "\n"; }
|
||||
schema s("");
|
||||
auto result = s.attach<author>("authors").and_then([&s] {
|
||||
return s.attach<book>("books");
|
||||
});
|
||||
|
||||
const auto c = pool.acquire();
|
||||
result = s.create(*c);
|
||||
if (!result) {
|
||||
std::cout << "error: " << result.err() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
author mc;
|
||||
mc.id = 1;
|
||||
mc.first_name = "Michael";
|
||||
mc.last_name = "Crichton";
|
||||
mc.date_of_birth = "19.8.1954";
|
||||
mc.year_of_birth = 1954;
|
||||
mc.distinguished = true;
|
||||
auto insert_authors_sql = insert()
|
||||
.into(AUTHOR)
|
||||
.values(mc)
|
||||
.execute(*c);
|
||||
|
||||
if (!insert_authors_sql) {
|
||||
std::cout << "error: " << insert_authors_sql.err() << std::endl;
|
||||
std::ignore = s.drop(*c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto authors_result = select(AUTHOR)
|
||||
.from(AUTHOR)
|
||||
.fetch_all(*c);
|
||||
|
||||
if (!authors_result) {
|
||||
std::cout << "error: " << authors_result.err() << std::endl;
|
||||
std::ignore = s.drop(*c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const auto &row: *authors_result) {
|
||||
std::cout << "Author " << row.at(AUTHOR.first_name.canonical_name()) << "\n";
|
||||
}
|
||||
//
|
||||
// auto update_authors_sql = c.query( s )
|
||||
// .update( qh::authors )
|
||||
@@ -233,19 +243,35 @@ int main() {
|
||||
// .values( {3, "Misery", mc.id, 1984} )
|
||||
// .execute();
|
||||
//
|
||||
// auto select_books_sql = c.query( s )
|
||||
// .select( qh::books.columns, {qh::authors.last_name} )
|
||||
// .from( qh::books )
|
||||
// .join_left( qh::authors )
|
||||
// .on( qh::books.author_id == qh::authors.id )
|
||||
// .where( qh::books.published_in < 2008 && qh::authors.last_name == "King" )
|
||||
// .group_by( qh::books.published_in )
|
||||
// .order_by( qh::books.title ).asc()
|
||||
// .limit( 5 )
|
||||
// .offset( 2 )
|
||||
// .fetch_all();
|
||||
//
|
||||
// for (const auto& r: select_books_sql) { std::cout << "R: " << r.at( qh::books.title ) << ", " << r.at( qh::authors.last_name ) << "\n"; }
|
||||
// auto select_books_sql = select( BOOK, {AUTHOR.last_name} )
|
||||
auto select_books_sql = select({BOOK.id, BOOK.title, BOOK.author_id, BOOK.published_in, AUTHOR.last_name})
|
||||
.from(BOOK)
|
||||
.join_left(AUTHOR)
|
||||
.on(BOOK.author_id == AUTHOR.id)
|
||||
.where(BOOK.published_in < 2008 && AUTHOR.last_name == "King")
|
||||
.group_by({BOOK.id, AUTHOR.last_name})
|
||||
.order_by(BOOK.title).asc()
|
||||
.limit(5)
|
||||
.offset(2)
|
||||
.fetch_all(*c);
|
||||
|
||||
if (!select_books_sql) {
|
||||
std::cout << "error: " << select_books_sql.err() << std::endl;
|
||||
std::ignore = s.drop(*c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const auto &r: *select_books_sql) {
|
||||
std::cout << "R: " << r.at(BOOK.title.canonical_name()) << ", " << r.at(AUTHOR.last_name.canonical_name()) << "\n";
|
||||
}
|
||||
|
||||
result = s.drop(*c);
|
||||
if (!result) {
|
||||
std::cout << "error: " << result.err() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// // SELECT book.title, book.id, book.author_id, book.published_in, author.name
|
||||
// // FROM book
|
||||
// // INNER JOIN author ON book.author_id = author.id
|
||||
@@ -262,24 +288,21 @@ int main() {
|
||||
//
|
||||
// std::cout << "SQL: " << drop_authors_sql << "\n";
|
||||
//
|
||||
// auto res = c.query( s )
|
||||
// .select( {qh::payload.id} )
|
||||
// .from( qh::payload )
|
||||
// .join_left( qh::job )
|
||||
// .on( qh::job.payload == qh::payload.id )
|
||||
// .where(
|
||||
// in( qh::payload.id, c.query( s )
|
||||
// .select( {qh::job.state} )
|
||||
// .from( qh::job )
|
||||
// .where( qh::job.state == job::job_state::Running )
|
||||
// ) &&
|
||||
// in( qh::payload.id, c.query( s )
|
||||
// .select( {qh::temporary_table.id} )
|
||||
// .from( qh::temporary_table ) )
|
||||
// )
|
||||
// .build();
|
||||
auto res = select({PAYLOAD.id})
|
||||
.from(PAYLOAD)
|
||||
.join_left(JOB)
|
||||
.on(JOB.payload == PAYLOAD.id)
|
||||
.where(
|
||||
in(PAYLOAD.id, select({JOB.state})
|
||||
.from(JOB)
|
||||
.where(JOB.state == job::job_state::Running)
|
||||
) &&
|
||||
in(PAYLOAD.id, select({TEMPORARY_TABLE.id})
|
||||
.from(TEMPORARY_TABLE)
|
||||
)
|
||||
).compile(c->dialect());
|
||||
// // .fetch_value<unsigned long>();
|
||||
// std::cout << "SQL: " << res.sql << "\n";
|
||||
std::cout << "SQL: " << res.sql << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
#include "matador/utils/basic_types.hpp"
|
||||
#include "matador/utils/constraints.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace object {
|
||||
|
||||
namespace utils = matador::utils;
|
||||
|
||||
class object;
|
||||
|
||||
enum class null_option_type : uint8_t {
|
||||
Nullable,
|
||||
NotNull
|
||||
};
|
||||
|
||||
class attribute {
|
||||
public:
|
||||
explicit attribute(std::string name); // NOLINT(*-explicit-constructor)
|
||||
|
||||
attribute(const attribute&) = default;
|
||||
attribute& operator=(const attribute&) = default;
|
||||
attribute(attribute&&) noexcept = default;
|
||||
attribute& operator=(attribute&&) noexcept = default;
|
||||
|
||||
attribute() = default;
|
||||
attribute(std::string name,
|
||||
utils::basic_type type,
|
||||
const utils::field_attributes& opts = matador::NullAttributes,
|
||||
null_option_type null_opt = null_option_type::NotNull)
|
||||
: name_(std::move(name)), type_(type), options_(opts), null_option_type_(null_opt) {}
|
||||
|
||||
[[nodiscard]] const std::string& name() const { return name_; }
|
||||
void name(const std::string& n) { name_ = n; }
|
||||
[[nodiscard]] const utils::field_attributes& options() const { return options_; }
|
||||
[[nodiscard]] utils::field_attributes& options();
|
||||
|
||||
[[nodiscard]] bool is_nullable() const { return null_option_type_ == null_option_type::Nullable; }
|
||||
[[nodiscard]] utils::basic_type type() const { return type_; }
|
||||
|
||||
[[nodiscard]] bool is_integer() const { return type_ >= utils::basic_type::Int8 && type_ <= utils::basic_type::UInt64; }
|
||||
[[nodiscard]] bool is_floating_point() const { return type_ == utils::basic_type::Float || type_ == utils::basic_type::Double; }
|
||||
[[nodiscard]] bool is_bool() const { return type_ == utils::basic_type::Boolean; }
|
||||
[[nodiscard]] bool is_string() const { return type_ == utils::basic_type::Text; }
|
||||
[[nodiscard]] bool is_varchar() const { return type_ == utils::basic_type::Varchar; }
|
||||
[[nodiscard]] bool is_date() const { return type_ == utils::basic_type::Date; }
|
||||
[[nodiscard]] bool is_time() const { return type_ == utils::basic_type::Time; }
|
||||
[[nodiscard]] bool is_blob() const { return type_ == utils::basic_type::Blob; }
|
||||
[[nodiscard]] bool is_null() const { return type_ == utils::basic_type::Null; }
|
||||
|
||||
private:
|
||||
friend class object;
|
||||
|
||||
std::string name_;
|
||||
std::string alias_;
|
||||
utils::basic_type type_{utils::basic_type::Null};
|
||||
utils::field_attributes options_;
|
||||
null_option_type null_option_type_{null_option_type::NotNull};
|
||||
|
||||
object* parent_{nullptr};
|
||||
};
|
||||
|
||||
class constraint {
|
||||
public:
|
||||
constraint() = default;
|
||||
|
||||
explicit constraint(std::string name) : name_(std::move(name)) {}
|
||||
[[nodiscard]] const std::string& name() const { return name_; }
|
||||
|
||||
private:
|
||||
friend class object;
|
||||
|
||||
std::string name_;
|
||||
std::string attribute_name_;
|
||||
utils::constraints options_{utils::constraints::None};
|
||||
|
||||
object* parent_{nullptr};
|
||||
attribute* attribute_{nullptr};
|
||||
};
|
||||
|
||||
class object {
|
||||
public:
|
||||
explicit object(std::string name, std::string alias = "");
|
||||
|
||||
void add_attribute(attribute attr) {
|
||||
auto &ref = attributes_.emplace_back(std::move(attr));
|
||||
ref.parent_ = this;
|
||||
}
|
||||
|
||||
static const attribute& create_attribute(std::string name, object& obj) {
|
||||
attribute attr{std::move(name)};
|
||||
attr.parent_ = &obj;
|
||||
return obj.attributes_.emplace_back(std::move(attr));
|
||||
}
|
||||
|
||||
void add_constraint(constraint c) {
|
||||
auto &ref = constraints_.emplace_back(std::move(c));
|
||||
ref.parent_ = this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& name() const { return name_; }
|
||||
[[nodiscard]] const std::string& alias() const { return alias_; }
|
||||
|
||||
[[nodiscard]] bool has_attributes() const { return attributes_.empty(); }
|
||||
[[nodiscard]] size_t attribute_count() const { return attributes_.size(); }
|
||||
[[nodiscard]] const std::vector<attribute>& attributes() const { return attributes_; }
|
||||
|
||||
[[nodiscard]] bool has_constraints() const { return constraints_.empty(); }
|
||||
[[nodiscard]] size_t constraint_count() const { return constraints_.size(); }
|
||||
[[nodiscard]] const std::vector<constraint>& constraints() const { return constraints_; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::string alias_;
|
||||
|
||||
std::vector<attribute> attributes_;
|
||||
std::vector<constraint> constraints_;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class typed_object : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
Type as(std::string alias) { return Type{std::move(alias)}; }
|
||||
};
|
||||
|
||||
struct column_builder {
|
||||
explicit column_builder(std::string column_name)
|
||||
: column_name( std::move(column_name) ) {
|
||||
}
|
||||
|
||||
column_builder& not_null() {
|
||||
return *this;
|
||||
}
|
||||
|
||||
column_builder& primary_key() {
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator attribute() const {
|
||||
return attribute{column_name};
|
||||
}
|
||||
|
||||
std::string column_name;
|
||||
};
|
||||
|
||||
column_builder column(std::string name) {
|
||||
return column_builder(std::move(name));
|
||||
}
|
||||
|
||||
namespace matador::sql {
|
||||
struct constraint {
|
||||
std::string name;
|
||||
};
|
||||
}
|
||||
|
||||
struct constraint_builder {
|
||||
constraint_builder& constraint(std::string name) {
|
||||
constraint_name = std::move(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constraint_builder& primary_key(std::string name) {
|
||||
pk_column_name = std::move(name);
|
||||
return *this;
|
||||
}
|
||||
constraint_builder& foreign_key(std::string name) {
|
||||
fk_column_name = std::move(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constraint_builder& references(std::string table, std::string column) {
|
||||
this->table_name = std::move(table);
|
||||
this->column_name = std::move(column);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator matador::sql::constraint() const {
|
||||
return matador::sql::constraint{constraint_name};
|
||||
}
|
||||
|
||||
std::string constraint_name;
|
||||
std::string pk_column_name;
|
||||
std::string fk_column_name;
|
||||
std::string table_name;
|
||||
std::string column_name;
|
||||
};
|
||||
|
||||
constraint_builder constraint(std::string name) {
|
||||
constraint_builder builder;
|
||||
return builder.constraint(std::move(name));
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
std::string column_prefix(typed_object<Type> *tab) {
|
||||
if (!tab || tab->empty()) {
|
||||
return "";
|
||||
}
|
||||
if (!tab->alias().empty()) {
|
||||
return tab->alias();
|
||||
}
|
||||
return tab->name();
|
||||
}
|
||||
|
||||
struct table_builder {
|
||||
explicit table_builder(std::string name)
|
||||
: table_name( std::move(name) ) {}
|
||||
|
||||
table_builder& as(std::string table_alias) {
|
||||
this->alias = std::move(table_alias);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator object() const {
|
||||
return object{table_name, alias};
|
||||
}
|
||||
std::string table_name;
|
||||
std::string alias;
|
||||
};
|
||||
|
||||
table_builder table(std::string name) {
|
||||
return table_builder(std::move(name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef QUERY_RECIPE_HPP
|
||||
#define QUERY_RECIPE_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
#include "matador/object/collection.hpp"
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
#include "matador/object/many_to_many_relation.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace demo {
|
||||
|
||||
struct recipe;
|
||||
struct ingredient {
|
||||
unsigned int id{};
|
||||
std::string name;
|
||||
matador::object::collection<matador::object::object_ptr<demo::recipe>> recipes{};
|
||||
|
||||
ingredient()= default;
|
||||
ingredient(const unsigned int id, std::string name)
|
||||
: id(id), name(std::move(name)) {}
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, VarChar255);
|
||||
field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::CascadeAllFetchEager);
|
||||
}
|
||||
};
|
||||
|
||||
struct recipe {
|
||||
unsigned int id{};
|
||||
std::string name;
|
||||
matador::object::collection<matador::object::object_ptr<demo::ingredient>> ingredients{};
|
||||
|
||||
recipe()= default;
|
||||
recipe(const unsigned int id, std::string name)
|
||||
: id(id), name(std::move(name)) {}
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::primary_key(op, "id", id);
|
||||
field::attribute(op, "name", name, VarChar255);
|
||||
field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::CascadeAllFetchLazy);
|
||||
}
|
||||
};
|
||||
|
||||
class recipe_ingredient : public matador::object::many_to_many_relation<recipe, ingredient> {
|
||||
public:
|
||||
recipe_ingredient() : many_to_many_relation("recipe_id", "ingredient_id") {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_RECIPE_HPP
|
||||
@@ -0,0 +1,226 @@
|
||||
#include "matador/object/repository.hpp"
|
||||
#include "matador/object/collection.hpp"
|
||||
|
||||
#include "matador/sql/resolver_service.hpp"
|
||||
|
||||
#include "matador/logger/log_manager.hpp"
|
||||
|
||||
#include "author.hpp"
|
||||
#include "book.hpp"
|
||||
#include "recipe.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/*
|
||||
* node author
|
||||
*
|
||||
* relation_endpoints
|
||||
* 1. - field_name "books"
|
||||
* - type "has_many"
|
||||
* - node <author>
|
||||
* - foreign endpoint (1) "author"
|
||||
*
|
||||
* node book
|
||||
* 2. - field_name "author_id"
|
||||
* - type "belongs_to"
|
||||
* - node <book>
|
||||
* - foreign endpoint (2) "books"
|
||||
*
|
||||
* Attach process:
|
||||
*
|
||||
* attach<author>
|
||||
* - relation completer detects "has_many<book>"
|
||||
* - has_many<book> doesn't find <book> (not yet attached)
|
||||
* - create endpoint (1) without foreign endpoint
|
||||
* - create node many_to_many_relation<author, book>("author_id", "id")
|
||||
* - 3. create endpoint
|
||||
* - field name "author_id"
|
||||
* - type "belongs_to"
|
||||
* - node <many_to_many_relation<author, book>>
|
||||
* - foreign endpoint (1) "author
|
||||
* - set foreign endpoint of (1) to endpoint (3)
|
||||
* - register endpoint in authors node by type book
|
||||
* - attach (internal) node<many_to_many_relation<author, book>>
|
||||
*
|
||||
* attach<book>
|
||||
* - relation completer detects "belongs_to<author>"
|
||||
* - belongs_to<author> finds <book>
|
||||
* - try to find relation endpoint of type book in authors node
|
||||
* - if endpoint was found
|
||||
* - validate node in endpoint
|
||||
* - if node is of type many_to_many_relation
|
||||
* - detach node of endpoint
|
||||
* - update node in endpoint to <book>
|
||||
* - else
|
||||
* - create endpoint <book> (1)
|
||||
* - create endpoint <author> (2)
|
||||
* - set foreign endpoint of (1) to endpoint (2)
|
||||
* - set foreign endpoint of (2) to endpoint (1)
|
||||
* - check relation endpoints...
|
||||
*/
|
||||
|
||||
namespace demo {
|
||||
struct names {
|
||||
unsigned int id{};
|
||||
std::vector<std::string> names_list;
|
||||
|
||||
template<typename Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::has_many(op, "name_list", names_list, "names_id", matador::utils::fetch_type::Eager);
|
||||
}
|
||||
};
|
||||
|
||||
struct user;
|
||||
struct profile {
|
||||
unsigned int id{};
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
matador::object::object_ptr<demo::user> user;
|
||||
|
||||
template<typename Operator>
|
||||
void process(Operator &op) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::attribute( op, "first_name", first_name, VarChar255 );
|
||||
field::attribute( op, "last_name", last_name, VarChar255 );
|
||||
field::belongs_to( op, "user_id", user, utils::CascadeNoneFetchLazy );
|
||||
}
|
||||
};
|
||||
struct user {
|
||||
unsigned int id{};
|
||||
std::string username;
|
||||
matador::object::object_ptr<demo::profile> profile;
|
||||
|
||||
template<typename Operator>
|
||||
void process(Operator &op) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::attribute( op, "username", username, VarChar255 );
|
||||
field::has_one(op, "profiles", profile, "profile_id", utils::CascadeNoneFetchLazy );
|
||||
}
|
||||
};
|
||||
|
||||
struct person {
|
||||
unsigned int id{};
|
||||
std::string name;
|
||||
|
||||
template<typename Operator>
|
||||
void process(Operator &op) {
|
||||
using namespace matador;
|
||||
namespace field = matador::access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::attribute( op, "name", name, VarChar255 );
|
||||
}
|
||||
};
|
||||
|
||||
struct person_repo {
|
||||
unsigned int id{};
|
||||
matador::object::collection<matador::object::object_ptr<person>> person_list;
|
||||
|
||||
template<typename Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::has_many( op, "person_list", person_list, "person_id", matador::utils::CascadeNoneFetchLazy );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
using namespace demo;
|
||||
using namespace matador;
|
||||
|
||||
int main() {
|
||||
// logger::default_min_log_level(logger::log_level::LVL_DEBUG);
|
||||
// logger::add_log_sink(logger::create_stdout_sink());
|
||||
|
||||
sql::resolver_service rs;
|
||||
|
||||
std::weak_ptr cr = rs.joined_collection_resolver<object::object_ptr<names>>( typeid(person), "names" );
|
||||
utils::identifier id{1};
|
||||
|
||||
object::collection_proxy cp(cr, id);
|
||||
{
|
||||
// has_many with builtin-type
|
||||
object::repository repo;
|
||||
|
||||
auto result = repo.attach<names>("names");
|
||||
|
||||
repo.dump(std::cout);
|
||||
}
|
||||
{
|
||||
// has_many with foreign without belongs_to
|
||||
object::repository repo;
|
||||
|
||||
auto result = repo.attach<person_repo>("person_repo")
|
||||
.and_then([&repo] { return repo.attach<person>("persons"); });
|
||||
|
||||
repo.dump(std::cout);
|
||||
}
|
||||
{
|
||||
// has_many with foreign without belongs_to
|
||||
object::repository repo;
|
||||
|
||||
auto result = repo.attach<person>("persons")
|
||||
.and_then([&repo] { return repo.attach<person_repo>("person_repo"); });
|
||||
|
||||
repo.dump(std::cout);
|
||||
}
|
||||
{
|
||||
// has_many to belongs_to
|
||||
object::repository repo;
|
||||
|
||||
auto result = repo.attach<author>("authors")
|
||||
.and_then([&repo] { return repo.attach<book>("books"); });
|
||||
|
||||
repo.dump(std::cout);
|
||||
}
|
||||
{
|
||||
// belongs_to to has_many
|
||||
object::repository repo;
|
||||
|
||||
auto result = repo.attach<book>("books")
|
||||
.and_then([&repo] { return repo.attach<author>("authors"); });
|
||||
|
||||
repo.dump(std::cout);
|
||||
}
|
||||
{
|
||||
// has_many_to_many (with join columns first)
|
||||
object::repository repo;
|
||||
|
||||
auto result = repo.attach<demo::ingredient>("ingredients")
|
||||
.and_then([&repo] { return repo.attach<recipe>("recipes"); });
|
||||
|
||||
repo.dump(std::cout);
|
||||
}
|
||||
{
|
||||
// has_many_to_many (with join columns last)
|
||||
object::repository repo;
|
||||
|
||||
auto result = repo.attach<demo::recipe>("recipes")
|
||||
.and_then([&repo] { return repo.attach<ingredient>("ingredients"); });
|
||||
|
||||
repo.dump(std::cout);
|
||||
}
|
||||
{
|
||||
// belongs_to to has_one
|
||||
object::repository repo;
|
||||
|
||||
auto result = repo.attach<profile>("profiles")
|
||||
.and_then([&repo] { return repo.attach<user>("users"); });
|
||||
|
||||
repo.dump(std::cout);
|
||||
}
|
||||
{
|
||||
// has_one to belongs_to
|
||||
object::repository repo;
|
||||
|
||||
auto result = repo.attach<user>("users")
|
||||
.and_then([&repo] { return repo.attach<profile>("profiles"); });
|
||||
|
||||
repo.dump(std::cout);
|
||||
}
|
||||
}
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
#include "work/admin/CollectionCenter.hpp"
|
||||
#include "work/admin/InternalUserDirectory.hpp"
|
||||
#include "work/admin/LdapGroupSchemaSettings.hpp"
|
||||
#include "work/admin/LdapImportSettings.hpp"
|
||||
#include "work/admin/LdapUserDirectory.hpp"
|
||||
#include "work/admin/LdapUserSchemaSettings.hpp"
|
||||
#include "work/admin/LoginHistory.hpp"
|
||||
#include "work/admin/Scenario.hpp"
|
||||
#include "work/admin/User.hpp"
|
||||
#include "work/admin/UserDirectory.hpp"
|
||||
#include "work/admin/UserSession.hpp"
|
||||
|
||||
#include "work/jobs/Job.hpp"
|
||||
#include "work/jobs/Task.hpp"
|
||||
#include "work/jobs/Payload.hpp"
|
||||
#include "work/jobs/IdPayload.hpp"
|
||||
#include "work/jobs/IdListPayload.hpp"
|
||||
|
||||
#include "matador/utils/default_type_traits.hpp"
|
||||
#include "matador/object/repository.hpp"
|
||||
|
||||
#include "matador/sql/connection.hpp"
|
||||
|
||||
#include "matador/query/schema.hpp"
|
||||
|
||||
#include "matador/query/session.hpp"
|
||||
|
||||
template <> struct matador::utils::data_type_traits<work::core::UserInfo, void> {
|
||||
static basic_type type(std::size_t /*size*/) { return basic_type::UInt64; }
|
||||
static void read_value(attribute_reader &/*reader*/, const char * /*id*/, size_t /*index*/, work::core::UserInfo &/*value*/) {
|
||||
|
||||
}
|
||||
static void bind_value(attribute_writer &/*binder*/, size_t /*index*/, work::core::UserInfo &/*value*/) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
using namespace matador;
|
||||
using namespace work::models;
|
||||
|
||||
// Proposal for polymorphic classes:
|
||||
// object_ptr::as<Type> does the following checks;
|
||||
//
|
||||
// 1. The requested type has super class
|
||||
// 2. Super class has discriminator column defined
|
||||
// 3. Super class has discriminator value defined
|
||||
// 4. Discriminator value is mapped to the requested type
|
||||
//
|
||||
// If all checks succeed, the requested is fetched from
|
||||
// the database.
|
||||
// schema.attach_as_base<jobs::Payload>("payloads", make_polymorph("type"));
|
||||
// schema.attach<jobs::Payload, jobs::IdPayload>("id_list_payloads", make_polymorph_type("IdPayload"));
|
||||
// schema.attach<jobs::Payload, jobs::IdListPayload>("id_payloads", make_polymorph_type("IdListPayload"));
|
||||
// object::object_ptr<jobs::Payload> payload;
|
||||
// auto result = payload.as<jobs::IdPayload>();
|
||||
// if (result.is_ok()) {
|
||||
// const auto& is_payload = result.value();
|
||||
// // Use requested type
|
||||
// id_payload->id = 1;
|
||||
// }
|
||||
// payload.is_polymorphic();
|
||||
// payload.is_polymorphic_type<jobs::IdPayload>();
|
||||
|
||||
int main() {
|
||||
logger::default_min_log_level(logger::log_level::Debug);
|
||||
logger::add_log_sink(logger::create_stdout_sink());
|
||||
|
||||
// sql::connection_pool<sql::connection> pool("postgres://news:news@127.0.0.1:15432/matador", 4);
|
||||
sql::connection_pool pool("postgres://test:test123!@127.0.0.1:15442/matador", 4);
|
||||
|
||||
utils::message_bus bus;
|
||||
// query::schema ses({bus, pool});
|
||||
query::schema admin_schema("Administration");
|
||||
|
||||
auto result = admin_schema.attach<admin::CollectionCenter>("collection_centers")
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::UserDirectory>("user_directories"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::LdapGroupSchemaSettings>("ldap_group_schema_settings"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::LdapImportSettings>("ldap_import_settings"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::LdapUserSchemaSettings>("ldap_user_schema_settings"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::InternalUserDirectory, admin::UserDirectory>("internal_user_directories"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::LdapUserDirectory, admin::UserDirectory>("ldap_user_directories"); } )
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::LoginHistory>("login_histories"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::Scenario>("scenarios"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::User>("users"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<admin::UserSession>("user_sessions"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<jobs::Job>("jobs"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<jobs::Payload>("payloads"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<jobs::IdPayload, jobs::Payload>("id_list_payloads"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<jobs::IdListPayload, jobs::Payload>("id_payloads"); })
|
||||
.and_then([&admin_schema] { return admin_schema.attach<jobs::Task>("tasks"); })
|
||||
.and_then([&admin_schema, &pool] { return admin_schema.create( *pool.acquire() ); })
|
||||
;
|
||||
admin_schema.dump(std::cout);
|
||||
|
||||
if (!result) {
|
||||
std::cout << "error: " << result.err().message() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto conn = pool.acquire();
|
||||
// result = admin_schema.create(*conn);
|
||||
// if (!result) {
|
||||
// std::cout << "error: " << result.err() << std::endl;
|
||||
// return 0;
|
||||
// }
|
||||
// result = admin_schema.drop(*conn);
|
||||
// if (!result) {
|
||||
// std::cout << "error: " << result.err() << std::endl;
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
/*
|
||||
const auto statement = QString( "SELECT %5.%6, %1.%2 FROM %3 %1, %4 %5 WHERE %1.%2=%5.%6 AND %7" )
|
||||
.arg( payloadTableAliasName,
|
||||
payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ),
|
||||
Payload::staticSqlMetaInfo().tableName(),
|
||||
Task::staticSqlMetaInfo().tableName(),
|
||||
taskTableAliasName,
|
||||
taskSqlMetaInfo.columnName( Task::Attributes::payloadAttributeId() ),
|
||||
filter->idSelectStatement( taskTableAliasName ) );
|
||||
*/
|
||||
|
||||
using namespace matador::query::meta;
|
||||
using namespace matador::utils;
|
||||
|
||||
const auto payloads = PAYLOAD.as( "payloads" );
|
||||
const auto tasks = TASK.as( "tasks" );
|
||||
|
||||
const auto stmt1 = query::select( { tasks.payload, payloads.id } )
|
||||
.from( tasks, payloads )
|
||||
.where ( payloads.id == tasks.payload )
|
||||
.str( conn->dialect() );
|
||||
|
||||
|
||||
/*
|
||||
const auto temporaryTable = status.addChildExc( fillTemporaryTable( idsInsidePayload ) );
|
||||
const auto lookUpTableName = temporaryTable->tableName();
|
||||
|
||||
const auto& jobSqlMetaInfo = Job::staticSqlMetaInfo();
|
||||
const auto& payloadSqlMetaInfo = Payload::staticSqlMetaInfo();
|
||||
const auto& idPayloadSqlMetaInfo = IdPayload::staticSqlMetaInfo();
|
||||
|
||||
const auto statement = QString( "SELECT iPT.%1 FROM %2 iPT INNER JOIN %3 pT ON iPT.%4 = pT.%5 WHERE pT.%5 IN (SELECT %6 FROM %7 WHERE %8=?) AND iPT.%1 IN (SELECT %9 FROM %10)" )
|
||||
.arg( idPayloadSqlMetaInfo.columnName( IdPayload::Attributes::payloadIdAttributeId() ) ) // %1
|
||||
.arg( idPayloadSqlMetaInfo.tableName() ) // %2
|
||||
.arg( payloadSqlMetaInfo.tableName() ) // %3
|
||||
.arg( idPayloadSqlMetaInfo.columnName( IdPayload::Attributes::idAttributeId() ) ) // %4
|
||||
.arg( payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ) ) // %5
|
||||
.arg( jobSqlMetaInfo.columnName( Job::Attributes::payloadAttributeId() ) ) // %6
|
||||
.arg( jobSqlMetaInfo.tableName() ) // %7
|
||||
.arg( jobSqlMetaInfo.columnName( Job::Attributes::stateSqlAttributeId() ) ) // %8
|
||||
.arg( lookUpColName() ) // %9
|
||||
.arg( lookUpTableName ); // %10
|
||||
*/
|
||||
|
||||
query::table_column temp_id_col("ID");
|
||||
query::table temporary("TempTable", { temp_id_col } );
|
||||
|
||||
const auto iPT = ID_PAYLOAD.as("iPT");
|
||||
const auto pT = PAYLOAD.as("pT");
|
||||
|
||||
const auto stmt2 = query::select({ iPT.payload_id })
|
||||
.from( iPT )
|
||||
.join_left( pT ).on( iPT.id == pT.id )
|
||||
.where(
|
||||
in( pT.id, query::select({ JOB.payload }).from( JOB ).where( JOB.state == _) ) &&
|
||||
in( iPT.payload_id, query::select({ temp_id_col }).from( temporary ) )
|
||||
)
|
||||
.str( conn->dialect() );
|
||||
|
||||
// const QString statement = QString( "SELECT %5 FROM %1 "
|
||||
// "LEFT JOIN (SELECT %2, MAX(%3) AS lastSuccessfulLogin FROM %1 WHERE %4 = ? GROUP BY %2) ll "
|
||||
// "ON %1.%2 = ll.%2 "
|
||||
// "WHERE (lastSuccessfulLogin IS NULL OR %3 <= lastSuccessfulLogin) AND %3 < ?" )
|
||||
// .arg( LoginHistory::staticSqlMetaInfo().tableName() )
|
||||
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::userAttributeId() ) )
|
||||
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::timestampAttributeId() ) )
|
||||
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::failReasonSqlAttributeId() ) )
|
||||
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::idAttributeId() ) );
|
||||
|
||||
const auto q = query::select({LOGIN_HISTORY.client, query::maximum(LOGIN_HISTORY.login_time).as( "lastSuccessfulLogin" ) } )
|
||||
.from( LOGIN_HISTORY )
|
||||
.where( LOGIN_HISTORY.fail_reason == _ )
|
||||
.group_by( LOGIN_HISTORY.client );
|
||||
// .as_table("ll");
|
||||
|
||||
const auto stmt3 = query::select({ LOGIN_HISTORY.client })
|
||||
.from( LOGIN_HISTORY )
|
||||
.join_left(q/*, "ll"*/ )
|
||||
.on( LOGIN_HISTORY.client == "ll.client" )
|
||||
.where( query::is_null("lastSuccessfulLogin") && LOGIN_HISTORY.login_time < "ll.lastSuccessfulLogin" )
|
||||
.str( conn->dialect() );
|
||||
|
||||
// const QString subQuery = QString( "SELECT MAX(%1) AS lastSuccessfulLogin, %3 FROM %2 "
|
||||
// "WHERE(%4 = ? OR %4 = ? OR %4 = ?) AND %3 = ? GROUP BY %3 " );
|
||||
//
|
||||
// const QString statement = QString( "SELECT MAX(t1.%1) AS LAST_FAILED_LOGIN FROM %2 t1 LEFT JOIN"
|
||||
// "(%0) t2 "
|
||||
// "ON t1.%3 = t2.%3 "
|
||||
// "WHERE(t2.lastSuccessfulLogin IS NULL OR t2.lastSuccessfulLogin < t1.%1) "
|
||||
// "AND (t1.%4 = ? OR t1.%4 = ?) "
|
||||
// "AND t1.%3 = ?" )
|
||||
// .arg( subQuery )
|
||||
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::timestampAttributeId() ) )
|
||||
// .arg( LoginHistory::staticSqlMetaInfo().tableName() )
|
||||
// .arg( columnFilterName )
|
||||
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::failReasonSqlAttributeId() ) );
|
||||
//
|
||||
// QSqlQuery query( sqlDatabase() );
|
||||
// if ( query.prepare( statement ) ) {
|
||||
// query.addBindValue( LoginHistory::FailReason::NoFailReason );
|
||||
// query.addBindValue( LoginHistory::FailReason::NoScenarioForUser );
|
||||
// query.addBindValue( LoginHistory::FailReason::UserLockedByAdmin );
|
||||
// query.addBindValue( columnFilterValue );
|
||||
// query.addBindValue( LoginHistory::FailReason::WrongPassword );
|
||||
// query.addBindValue( LoginHistory::FailReason::UnknownUserName );
|
||||
// query.addBindValue( columnFilterValue );
|
||||
|
||||
|
||||
// const auto temporaryTable = status.addChildExc( fillTemporaryTable( idsInsidePayload ) );
|
||||
//
|
||||
// const auto& jobSqlMetaInfo = Job::staticSqlMetaInfo();
|
||||
// const auto& payloadSqlMetaInfo = Payload::staticSqlMetaInfo();
|
||||
// const auto& idPayloadSqlMetaInfo = IdPayload::staticSqlMetaInfo();
|
||||
//
|
||||
// auto filter = std::make_unique<SqlTools::GenericFilter>( new SqlTools::AttributeDefaultFilter( jobSqlMetaInfo,
|
||||
// Job::Attributes::idAttributeId(),
|
||||
// Job::Attributes::idAttributeId(),
|
||||
// Job::Attributes::typeAttributeId(),
|
||||
// SqlTools::AttributeDefaultFilter::Operation::Equal,
|
||||
// jobName ),
|
||||
// new SqlTools::InFilter( payloadSqlMetaInfo.tableName(),
|
||||
// jobSqlMetaInfo.columnName( Job::Attributes::payloadAttributeId() ),
|
||||
// payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ),
|
||||
// new SqlTools::InFilter( idPayloadSqlMetaInfo.tableName(),
|
||||
// payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ),
|
||||
// idPayloadSqlMetaInfo.columnName( IdPayload::Attributes::idAttributeId() ),
|
||||
// new SqlTools::TemporaryInFilter( temporaryTable,
|
||||
// idPayloadSqlMetaInfo.columnName( IdPayload::Attributes::payloadIdAttributeId() ),
|
||||
// lookUpColName() ) ) ),
|
||||
// SqlTools::GenericFilter::Operation::And );
|
||||
//
|
||||
// if ( !jobStates.empty() ) {
|
||||
// auto it = jobStates.begin();
|
||||
// std::unique_ptr<SqlTools::GenericFilter> criteria = std::make_unique<SqlTools::AttributeDefaultFilter>( jobSqlMetaInfo,
|
||||
// Job::Attributes::idAttributeId(),
|
||||
// Job::Attributes::idAttributeId(),
|
||||
// Job::Attributes::stateSqlAttributeId(),
|
||||
// SqlTools::AttributeDefaultFilter::Operation::Equal,
|
||||
// *it++ );
|
||||
// for ( ; it != jobStates.end(); ++it ) {
|
||||
// criteria = std::make_unique<SqlTools::GenericFilter>( new SqlTools::AttributeDefaultFilter( jobSqlMetaInfo,
|
||||
// Job::Attributes::idAttributeId(),
|
||||
// Job::Attributes::idAttributeId(),
|
||||
// Job::Attributes::stateSqlAttributeId(),
|
||||
// SqlTools::AttributeDefaultFilter::Operation::Equal,
|
||||
// *it ),
|
||||
// criteria.release(),
|
||||
// SqlTools::GenericFilter::Operation::Or );
|
||||
// }
|
||||
//
|
||||
// filter = std::make_unique<SqlTools::GenericFilter>( criteria.release(), filter.release(), SqlTools::GenericFilter::Operation::And );
|
||||
// }
|
||||
//
|
||||
// const auto ids = status.addChildExc( this->ids( SqlManager::jobFilter, filter.get() ) );
|
||||
// SELECT
|
||||
// ID
|
||||
// FROM
|
||||
// NEWS_JOBS
|
||||
// WHERE
|
||||
// NEWS_JOBS.ID > ?
|
||||
// AND (NEWS_JOBS.TYPE = ?
|
||||
// AND ( NEWS_JOBS.PAYLOAD IN (
|
||||
// SELECT
|
||||
// ID
|
||||
// FROM
|
||||
// NEWS_PAYLOADS
|
||||
// WHERE
|
||||
// NEWS_PAYLOADS.ID IN (
|
||||
// SELECT
|
||||
// ID
|
||||
// FROM
|
||||
// NEWS_ID_PAYLOADS
|
||||
// WHERE
|
||||
// NEWS_ID_PAYLOADS.PAYLOAD_ID IN (SELECT ID FROM NEWS_TMP_3499274F12B644179DBD25BB60C48529)
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
|
||||
const auto stmt4 = query::select({JOB.id})
|
||||
.from(JOB)
|
||||
.where( JOB.id > _ && JOB.name == _ && in( JOB.payload, query::select({PAYLOAD.id}).from(PAYLOAD).where(
|
||||
in( PAYLOAD.id, query::select({ID_PAYLOAD.id}).from(ID_PAYLOAD).where(
|
||||
in( ID_PAYLOAD.id, query::select({temp_id_col}).from(temporary))
|
||||
) ) ) ) )
|
||||
.str( conn->dialect() );
|
||||
|
||||
// SELECT DISTINCT nj.ID
|
||||
// FROM NEWS_JOBS nj
|
||||
// JOIN NEWS_PAYLOADS np
|
||||
// ON nj.PAYLOAD = np.ID
|
||||
// JOIN NEWS_ID_PAYLOADS nip
|
||||
// ON np.ID = nip.ID
|
||||
// JOIN NEWS_TMP_3499274F12B644179DBD25BB60C48529 tmp
|
||||
// ON nip.PAYLOAD_ID = tmp.ID
|
||||
// WHERE nj.ID > ?
|
||||
// AND nj.TYPE = ?;
|
||||
|
||||
const auto stmt5 = query::select({JOB.id})
|
||||
.from(JOB)
|
||||
.join_left(PAYLOAD).on(JOB.payload == PAYLOAD.id)
|
||||
.join_left(ID_PAYLOAD).on(PAYLOAD.id == ID_PAYLOAD.id)
|
||||
.join_left(temporary).on(ID_PAYLOAD.id == temp_id_col)
|
||||
.where(JOB.id > _ && JOB.name == _)
|
||||
.str(conn->dialect());
|
||||
|
||||
std::cout << stmt1 << std::endl;
|
||||
std::cout << stmt2 << std::endl;
|
||||
std::cout << stmt3 << std::endl;
|
||||
std::cout << stmt4 << std::endl;
|
||||
std::cout << stmt5 << std::endl;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
SELECT
|
||||
"tasks"."payload", "payloads"."id"
|
||||
FROM
|
||||
"public"."tasks", "public"."payloads"
|
||||
WHERE
|
||||
"payloads"."id" = "tasks"."payload"
|
||||
|
||||
SELECT
|
||||
"iPT"."payload_id" FROM "public"."id_payloads" "iPT"
|
||||
LEFT JOIN
|
||||
"public"."payloads" "pT"
|
||||
ON
|
||||
"iPT"."id" = "pT"."id"
|
||||
WHERE ("pT"."id" IN (
|
||||
SELECT
|
||||
"jobs"."payload"
|
||||
FROM
|
||||
"public"."jobs"
|
||||
WHERE
|
||||
"jobs"."state" = $1
|
||||
) AND "iPT"."payload_id" IN (
|
||||
SELECT
|
||||
"ID"
|
||||
FROM "public"."TempTable"
|
||||
)
|
||||
)
|
||||
|
||||
SELECT
|
||||
"login_histories"."client"
|
||||
FROM
|
||||
"public"."login_histories"
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
"login_histories"."client", MAX(login_time) AS lastSuccessfulLogin
|
||||
FROM
|
||||
"public"."login_histories"
|
||||
WHERE
|
||||
"login_histories"."fail_reason" = $1
|
||||
GROUP BY
|
||||
"login_histories"."client"
|
||||
)
|
||||
ON
|
||||
"login_histories"."client" = 'll.client'
|
||||
WHERE
|
||||
("lastSuccessfulLogin" IS NULL AND "login_histories"."login_time" < 'll.lastSuccessfulLogin')
|
||||
*/
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef COLLECTION_CENTER_HPP
|
||||
#define COLLECTION_CENTER_HPP
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/object/collection.hpp"
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/enum_mapper.hpp"
|
||||
#include "matador/utils/fetch_type.hpp"
|
||||
#include "matador/utils/types.hpp"
|
||||
|
||||
#include "matador/query/meta_table_macro.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct User;
|
||||
|
||||
enum class CollectionCenterType : uint8_t {
|
||||
NoType,
|
||||
Core,
|
||||
Unit
|
||||
};
|
||||
|
||||
static const matador::utils::enum_mapper<CollectionCenterType> CollectionCenterTypeEnum({
|
||||
{CollectionCenterType::NoType, "NoType"},
|
||||
{CollectionCenterType::Core, "Core"},
|
||||
{CollectionCenterType::Unit, "Unit"}
|
||||
});
|
||||
|
||||
struct CollectionCenter : core::Model {
|
||||
std::string name;
|
||||
matador::utils::blob_type_t symbol;
|
||||
CollectionCenterType type{CollectionCenterType::NoType};
|
||||
matador::object::collection<matador::object::object_ptr<User>> users;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = matador::access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||
field::attribute( op, "symbol", symbol );
|
||||
field::attribute( op, "type", type );
|
||||
field::has_many( op, "collection_center_users", users, "users_id", utils::CascadeAllFetchLazy );
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
META_TABLE(collection_center, COLLECTION_CENTER, id, version, name, symbol, type)
|
||||
|
||||
#endif //COLLECTION_CENTER_HPP
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef INTERNAL_USER_DIRECTORY_HPP
|
||||
#define INTERNAL_USER_DIRECTORY_HPP
|
||||
|
||||
#include "UserDirectory.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct InternalUserDirectory : UserDirectory {
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
namespace field = matador::access;
|
||||
field::process( op, *matador::base_class<UserDirectory>( this ) );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INTERNAL_USER_DIRECTORY_HPP
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef LDAP_GROUP_SCHEMA_SETTINGS_HPP
|
||||
#define LDAP_GROUP_SCHEMA_SETTINGS_HPP
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct LdapUserDirectory;
|
||||
|
||||
struct LdapGroupSchemaSettings : core::Model {
|
||||
std::string group_object_filter{};
|
||||
std::string user_member_attribute{};
|
||||
matador::object::object_ptr<LdapUserDirectory> ldap_user_directory{};
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::has_one(op, "ldap_user_directory", ldap_user_directory, utils::CascadeAllFetchLazy);
|
||||
field::attribute( op, "group_object_filter", group_object_filter, VarChar511 );
|
||||
field::attribute( op, "user_member_attribute", user_member_attribute, VarChar511 );
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LDAP_GROUP_SCHEMA_SETTINGS_HPP
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef LDAP_IMPORT_SETTINGS_HPP
|
||||
#define LDAP_IMPORT_SETTINGS_HPP
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct LdapUserDirectory;
|
||||
|
||||
struct LdapImportSettings : core::Model {
|
||||
std::string default_role;
|
||||
unsigned int sync_interval;
|
||||
unsigned int network_timeout;
|
||||
matador::object::object_ptr<LdapUserDirectory> ldap_user_directory;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::has_one(op, "ldap_user_directory", ldap_user_directory, utils::CascadeAllFetchLazy);
|
||||
field::attribute( op, "default_role", default_role, VarChar511 );
|
||||
field::attribute( op, "sync_interval", sync_interval );
|
||||
field::attribute( op, "network_timeout", network_timeout );
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LDAP_IMPORT_SETTINGS_HPP
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef LDAP_USER_DIRECTORY_HPP
|
||||
#define LDAP_USER_DIRECTORY_HPP
|
||||
|
||||
#include "LdapUserSchemaSettings.hpp"
|
||||
#include "LdapGroupSchemaSettings.hpp"
|
||||
#include "LdapImportSettings.hpp"
|
||||
#include "UserDirectory.hpp"
|
||||
|
||||
#include "matador/object/collection.hpp"
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct LdapUserDirectory : UserDirectory {
|
||||
std::string schema_base_dn;
|
||||
std::string additional_user_base_dn;
|
||||
std::string additional_group_base_dn;
|
||||
matador::object::object_ptr<LdapUserSchemaSettings> user_schema_settings;
|
||||
matador::object::object_ptr<LdapGroupSchemaSettings> group_schema_settings;
|
||||
matador::object::object_ptr<LdapImportSettings> import_settings;
|
||||
matador::object::collection<std::string> users;
|
||||
matador::object::collection<std::string> groups;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::process( op, *matador::base_class<UserDirectory>( this ) );
|
||||
field::attribute( op, "schema_base_dn", schema_base_dn, VarChar511 );
|
||||
field::attribute( op, "additional_user_base_dn", additional_user_base_dn, VarChar511 );
|
||||
field::attribute( op, "additional_group_base_dn", additional_group_base_dn, VarChar511 );
|
||||
field::belongs_to( op, "user_schema_settings", user_schema_settings, utils::CascadeAllFetchLazy );
|
||||
field::belongs_to( op, "group_schema_settings", group_schema_settings, utils::CascadeAllFetchLazy );
|
||||
field::belongs_to( op, "import_settings", import_settings, utils::CascadeAllFetchLazy );
|
||||
field::has_many( op, "ldap_users", users, "users_id", utils::CascadeAllFetchEager );
|
||||
field::has_many( op, "ldap_groups", groups, "groups_id", utils::CascadeAllFetchEager );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //LDAP_USER_DIRECTORY_HPP
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef LDAP_USER_SCHEMA_SETTINGS_HPP
|
||||
#define LDAP_USER_SCHEMA_SETTINGS_HPP
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct LdapUserDirectory;
|
||||
|
||||
struct LdapUserSchemaSettings : core::Model {
|
||||
std::string user_object_filter{};
|
||||
std::string user_unique_id_attribute{};
|
||||
std::string user_member_of_attribute{};
|
||||
std::string user_name_attribute{};
|
||||
matador::object::object_ptr<LdapUserDirectory> ldap_user_directory{};
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::process(op, *matador::base_class<Model>( this ));
|
||||
field::has_one(op, "ldap_user_directory", ldap_user_directory, utils::CascadeAllFetchLazy);
|
||||
field::attribute(op, "user_object_filter", user_object_filter, VarChar511);
|
||||
field::attribute(op, "user_unique_id_attribute", user_unique_id_attribute, VarChar511);
|
||||
field::attribute(op, "user_member_of_attribute", user_member_of_attribute, VarChar511);
|
||||
field::attribute(op, "user_name_attribute", user_name_attribute, VarChar511);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LDAP_USER_SCHEMA_SETTINGS_HPP
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef LOCKTYPE_HPP
|
||||
#define LOCKTYPE_HPP
|
||||
|
||||
#include "matador/utils/enum_mapper.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
enum class LockType : uint8_t {
|
||||
NoLock,
|
||||
AdminLock,
|
||||
SystemLock
|
||||
};
|
||||
|
||||
static const matador::utils::enum_mapper<LockType> LockTypeEnum({
|
||||
{LockType::NoLock, "NoLock"},
|
||||
{LockType::AdminLock, "AdminLock"},
|
||||
{LockType::SystemLock, "SystemLock"}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
#endif //LOCKTYPE_HPP
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef LOGIN_HISTORY_HPP
|
||||
#define LOGIN_HISTORY_HPP
|
||||
|
||||
#include "CollectionCenter.hpp"
|
||||
#include "Scenario.hpp"
|
||||
#include "User.hpp"
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/query/meta_table_macro.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/enum_mapper.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
|
||||
enum class FailReason : uint8_t {
|
||||
NoFailReason,
|
||||
InternalError,
|
||||
NoPermissionToLogin,
|
||||
NoScenarioForUser,
|
||||
UnknownUserName,
|
||||
UserLockedByAdmin,
|
||||
UserLockedBySystem,
|
||||
UserLockedByAdminAndSystem,
|
||||
WrongPassword
|
||||
};
|
||||
|
||||
static const matador::utils::enum_mapper<FailReason> FailReasonEnum({
|
||||
{FailReason::NoFailReason, "NoFailReason"},
|
||||
{FailReason::InternalError, "InternalError"},
|
||||
{FailReason::NoPermissionToLogin, "NoPermissionToLogin"},
|
||||
{FailReason::NoScenarioForUser, "NoScenarioForUser"},
|
||||
{FailReason::UnknownUserName, "UnknownUserName"},
|
||||
{FailReason::UserLockedByAdmin, "UserLockedByAdmin"},
|
||||
{FailReason::UserLockedBySystem, "UserLockedBySystem"},
|
||||
{FailReason::UserLockedByAdminAndSystem, "UserLockedByAdminAndSystem"},
|
||||
{FailReason::WrongPassword, "WrongPassword"}
|
||||
});
|
||||
|
||||
struct LoginHistory : core::Model {
|
||||
matador::object::object_ptr<User> client;
|
||||
matador::object::object_ptr<Scenario> scenario;
|
||||
matador::object::object_ptr<CollectionCenter> collection_center;
|
||||
std::string login_name;
|
||||
FailReason fail_reason{FailReason::NoFailReason};
|
||||
matador::utils::timestamp_type_t login_time;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::process_base<Model>( op, *this );
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::belongs_to( op, "client", client, utils::CascadeAllFetchLazy );
|
||||
field::belongs_to( op, "scenario", scenario, utils::CascadeAllFetchLazy );
|
||||
field::belongs_to( op, "collection_center", collection_center, utils::CascadeAllFetchLazy );
|
||||
field::attribute( op, "login_name", login_name, VarChar511 );
|
||||
field::attribute( op, "fail_reason", fail_reason );
|
||||
field::attribute( op, "login_time", login_time );
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
META_TABLE( login_histories, LOGIN_HISTORY, id, version, client, scenario, collection_center, login_name, fail_reason, login_time )
|
||||
|
||||
#endif //LOGIN_HISTORY_HPP
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef SCENARIO_HPP
|
||||
#define SCENARIO_HPP
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/object/collection.hpp"
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/enum_mapper.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct CollectionCenter;
|
||||
|
||||
enum class ScenarioMode : uint8_t {
|
||||
InvalidMode,
|
||||
Exercise,
|
||||
Operation,
|
||||
};
|
||||
|
||||
static const matador::utils::enum_mapper<ScenarioMode> ScenarioModeEnum({
|
||||
{ScenarioMode::InvalidMode, "InvalidMode"},
|
||||
{ScenarioMode::Exercise, "Exercise"},
|
||||
{ScenarioMode::Operation, "Operation"}
|
||||
});
|
||||
|
||||
enum class ScenarioState : uint8_t {
|
||||
InvalidState,
|
||||
Active,
|
||||
Inactive
|
||||
};
|
||||
|
||||
static const matador::utils::enum_mapper<ScenarioState> ScenarioStateEnum({
|
||||
{ScenarioState::InvalidState, "InvalidState"},
|
||||
{ScenarioState::Active, "Active"},
|
||||
{ScenarioState::Inactive, "Inactive"}
|
||||
});
|
||||
|
||||
struct Scenario : core::Model {
|
||||
std::string name;
|
||||
ScenarioMode mode{ScenarioMode::InvalidMode};
|
||||
std::string description;
|
||||
matador::utils::blob_type_t symbol;
|
||||
ScenarioState state{ScenarioState::InvalidState};
|
||||
matador::object::collection<matador::object::object_ptr<CollectionCenter>> collection_center;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||
field::attribute( op, "mode", mode );
|
||||
field::attribute( op, "description", description, VarChar511 );
|
||||
field::attribute( op, "symbol", symbol );
|
||||
field::attribute( op, "state", state );
|
||||
field::has_many( op, "collection_center", collection_center, "scenario_id", utils::CascadeAllFetchLazy );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SCENARIO_HPP
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef USER_HPP
|
||||
#define USER_HPP
|
||||
|
||||
#include "LockType.hpp"
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/query/meta_table_macro.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
#include "matador/utils/types.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct UserDirectory;
|
||||
|
||||
struct User : core::Model {
|
||||
std::string name;
|
||||
matador::utils::blob_type_t symbol;
|
||||
std::string salt;
|
||||
std::string password;
|
||||
LockType lock_type{LockType::NoLock};
|
||||
matador::utils::timestamp_type_t locked_at;
|
||||
std::string lock_reason;
|
||||
std::string role;
|
||||
matador::object::object_ptr<UserDirectory> user_directory;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||
field::attribute( op, "symbol", symbol );
|
||||
field::attribute( op, "salt", salt, VarChar511 );
|
||||
field::attribute( op, "password", password, VarChar511 );
|
||||
field::attribute( op, "lock_type", lock_type );
|
||||
field::attribute( op, "locked_at", locked_at );
|
||||
field::attribute( op, "lock_reason", lock_reason, VarChar511 );
|
||||
field::attribute( op, "role", role, VarChar63 );
|
||||
field::belongs_to( op, "user_directory", user_directory, utils::CascadeAllFetchLazy );
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
META_TABLE(users, USER, id, version, name, symbol, salt, password, lock_type, locked_at, lock_reason, role, user_directory)
|
||||
|
||||
#endif //USER_HPP
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef USER_DIRECTORY_HPP
|
||||
#define USER_DIRECTORY_HPP
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct UserDirectory : core::Model {
|
||||
std::string name;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
namespace field = matador::access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::attribute( op, "name", name, matador::VarChar511 );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //USER_DIRECTORY_HPP
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef USER_SESSION_HPP
|
||||
#define USER_SESSION_HPP
|
||||
|
||||
#include "CollectionCenter.hpp"
|
||||
#include "Scenario.hpp"
|
||||
#include "User.hpp"
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::admin {
|
||||
struct UserSession : core::Model {
|
||||
matador::object::object_ptr<User> client;
|
||||
matador::object::object_ptr<Scenario> scenario;
|
||||
matador::object::object_ptr<CollectionCenter> collection_center;
|
||||
matador::utils::timestamp_type_t offline_since;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::belongs_to( op, "client", client, utils::CascadeAllFetchLazy );
|
||||
field::belongs_to( op, "scenario", scenario, utils::CascadeAllFetchLazy );
|
||||
field::belongs_to( op, "collection_center", collection_center, utils::CascadeAllFetchLazy );
|
||||
field::attribute( op, "offline_since", offline_since );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //USER_SESSION_HPP
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef MODEL_HPP
|
||||
#define MODEL_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace work::core {
|
||||
struct Model {
|
||||
int64_t id{};
|
||||
uint64_t version{};
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
namespace field = matador::access;
|
||||
field::primary_key( op, "id", id );
|
||||
field::revision( op, "version", version );
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif //MODEL_HPP
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef USERINFO_HPP
|
||||
#define USERINFO_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
namespace work::core {
|
||||
struct UserInfo {
|
||||
unsigned long long user_session_id;
|
||||
std::string user_role;
|
||||
std::string database_name;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
namespace field = matador::access;
|
||||
using namespace matador;
|
||||
field::attribute( op, "user_session_id", user_session_id );
|
||||
field::attribute( op, "user_role", user_role, VarChar255 );
|
||||
field::attribute( op, "database_name", database_name, VarChar255 );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //USERINFO_HPP
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef ID_LIST_PAYLOAD_HPP
|
||||
#define ID_LIST_PAYLOAD_HPP
|
||||
|
||||
#include "Payload.hpp"
|
||||
|
||||
#include "matador/object/collection.hpp"
|
||||
|
||||
#include "matador/query/meta_table_macro.hpp"
|
||||
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::jobs {
|
||||
struct IdListPayload : Payload {
|
||||
matador::object::collection<uint64_t> payload_ids;
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
namespace field = matador::access;
|
||||
field::process( op, *matador::base_class<Payload>( this ) );
|
||||
field::has_many( op, "payload_ids", payload_ids, "payload_id", matador::utils::CascadeNoneFetchLazy );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
META_TABLE(id_list_payloads, ID_LIST_PAYLOAD, id, version, type, job)
|
||||
|
||||
#endif //ID_LIST_PAYLOAD_HPP
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef ID_PAYLOAD_HPP
|
||||
#define ID_PAYLOAD_HPP
|
||||
|
||||
#include "Payload.hpp"
|
||||
|
||||
#include "matador/query/meta_table_macro.hpp"
|
||||
|
||||
namespace work::models::jobs {
|
||||
struct IdPayload : Payload {
|
||||
uint64_t payload_id{};
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
namespace field = matador::access;
|
||||
field::process( op, *matador::base_class<Payload>( this ) );
|
||||
field::attribute( op, "payload_id", payload_id );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
META_TABLE(id_payloads, ID_PAYLOAD, id, version, type, job, payload_id)
|
||||
|
||||
#endif //ID_PAYLOAD_HPP
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef JOB_HPP
|
||||
#define JOB_HPP
|
||||
|
||||
#include "JobMode.hpp"
|
||||
#include "JobState.hpp"
|
||||
#include "Payload.hpp"
|
||||
#include "Task.hpp"
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
#include "../core/UserInfo.hpp"
|
||||
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/query/meta_table_macro.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::jobs {
|
||||
|
||||
struct Job : core::Model {
|
||||
std::string name;
|
||||
std::string description;
|
||||
JobState state;
|
||||
JobMode mode;
|
||||
matador::utils::timestamp_type_t created_at;
|
||||
matador::object::object_ptr<Payload> payload;
|
||||
matador::object::object_ptr<Task> task;
|
||||
core::UserInfo user_info;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = matador::access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||
field::attribute( op, "description", description, VarChar1023 );
|
||||
field::attribute( op, "state", state );
|
||||
field::attribute( op, "mode", mode );
|
||||
field::attribute( op, "created_at", created_at );
|
||||
field::has_one(op, "payload", payload, utils::CascadeAllFetchLazy );
|
||||
field::belongs_to( op, "task", task, utils::CascadeAllFetchLazy );
|
||||
field::attribute( op, "user_info", user_info );
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
META_TABLE(jobs, JOB, id, version, name, description, state, mode, created_at, payload, task, user_info)
|
||||
|
||||
#endif //JOB_HPP
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef JOB_MODE_HPP
|
||||
#define JOB_MODE_HPP
|
||||
|
||||
#include "matador/utils/enum_mapper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace work::models::jobs {
|
||||
enum class JobMode : uint8_t {
|
||||
Background,
|
||||
Foreground
|
||||
};
|
||||
|
||||
static const matador::utils::enum_mapper<JobMode> JobModeEnum({
|
||||
{JobMode::Background, "Background"},
|
||||
{JobMode::Foreground, "Foreground"}
|
||||
});
|
||||
}
|
||||
|
||||
#endif //JOB_MODE_HPP
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef JOB_STATE_HPP
|
||||
#define JOB_STATE_HPP
|
||||
|
||||
#include "matador/utils/enum_mapper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace work::models::jobs {
|
||||
enum class JobState : uint8_t {
|
||||
Pending,
|
||||
Running,
|
||||
Succeeded,
|
||||
Failed,
|
||||
Cancelled
|
||||
};
|
||||
|
||||
static const matador::utils::enum_mapper<JobState> JobStateEnum({
|
||||
{JobState::Pending, "Pending"},
|
||||
{JobState::Running, "Running"},
|
||||
{JobState::Succeeded, "Succeeded"},
|
||||
{JobState::Failed, "Failed"},
|
||||
{JobState::Cancelled, "Cancelled"}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //JOB_STATE_HPP
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef PAYLOAD_HPP
|
||||
#define PAYLOAD_HPP
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/query/meta_table_macro.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::jobs {
|
||||
struct Job;
|
||||
struct Payload : core::Model {
|
||||
std::string type{};
|
||||
matador::object::object_ptr<Job> job{};
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = matador::access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::attribute( op, "type", type, VarChar255 );
|
||||
field::belongs_to( op, "job", job, utils::CascadeAllFetchLazy );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
META_TABLE(payloads, PAYLOAD, id, version, type, job)
|
||||
|
||||
#endif //PAYLOAD_HPP
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef TASK_HPP
|
||||
#define TASK_HPP
|
||||
|
||||
#include "TaskState.hpp"
|
||||
|
||||
#include "../core/Model.hpp"
|
||||
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/query/meta_table_macro.hpp"
|
||||
|
||||
#include "matador/utils/base_class.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
namespace work::models::jobs {
|
||||
struct Task : core::Model {
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string job_name;
|
||||
TaskState state;
|
||||
matador::object::object_ptr<Payload> payload;
|
||||
JobMode job_mode;
|
||||
matador::utils::timestamp_type_t start_delay;
|
||||
matador::utils::timestamp_type_t interval;
|
||||
uint64_t user_session_id;
|
||||
|
||||
template<typename Operator>
|
||||
void process( Operator& op ) {
|
||||
using namespace matador;
|
||||
namespace field = matador::access;
|
||||
field::process( op, *matador::base_class<Model>( this ) );
|
||||
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||
field::attribute( op, "description", description, VarChar1023 );
|
||||
field::attribute( op, "job_name", job_name, VarChar511 );
|
||||
field::attribute( op, "state", state );
|
||||
field::belongs_to( op, "payload", payload, utils::CascadeAllFetchLazy );
|
||||
field::attribute( op, "job_mode", job_mode );
|
||||
field::attribute( op, "start_delay", start_delay );
|
||||
field::attribute( op, "interval", interval );
|
||||
field::attribute( op, "user_session_id", user_session_id );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
META_TABLE(tasks, TASK, id, version, name, description, job_name, state, payload, job_mode, start_delay, interval, user_session_id)
|
||||
|
||||
#endif //TASK_HPP
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef TASK_STATE_HPP
|
||||
#define TASK_STATE_HPP
|
||||
|
||||
#include "matador/utils/enum_mapper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace work::models::jobs {
|
||||
enum class TaskState : uint8_t {
|
||||
Active,
|
||||
Inactive
|
||||
};
|
||||
|
||||
static const matador::utils::enum_mapper<TaskState> TaskStateEnum({
|
||||
{TaskState::Active, "Active"},
|
||||
{TaskState::Inactive, "Inactive"}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //TASK_STATE_HPP
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef MATADOR_BASIC_FILE_SINK_HPP
|
||||
#define MATADOR_BASIC_FILE_SINK_HPP
|
||||
|
||||
#include "matador/logger/log_sink.hpp"
|
||||
|
||||
namespace matador::logger {
|
||||
|
||||
/**
|
||||
* @brief A base class for the file-stream-based sinks
|
||||
*
|
||||
* This class acts like a base class for all
|
||||
* concrete sinks working with a file stream to write
|
||||
* the log message.
|
||||
*/
|
||||
class basic_file_sink : public log_sink
|
||||
{
|
||||
protected:
|
||||
basic_file_sink() = default;
|
||||
|
||||
/**
|
||||
* Creates a basic_file_sink with a given file stream
|
||||
*
|
||||
* @param f File stream to write on
|
||||
*/
|
||||
explicit basic_file_sink(FILE *f);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Writes the log message to the internal
|
||||
* file stream.
|
||||
*
|
||||
* @param message The message to write
|
||||
* @param size The size of the message
|
||||
*/
|
||||
void write(const char *message, size_t size) override;
|
||||
|
||||
/**
|
||||
* Closes the internal file stream.
|
||||
*/
|
||||
void close() override;
|
||||
|
||||
protected:
|
||||
/// @cond MATADOR_DEV
|
||||
FILE *stream = nullptr;
|
||||
/// @endcond
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_BASIC_FILE_SINK_HPP
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef MATADOR_FILE_SINK_HPP
|
||||
#define MATADOR_FILE_SINK_HPP
|
||||
|
||||
#include "matador/logger/basic_file_sink.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace matador::logger {
|
||||
|
||||
/**
|
||||
* @brief A file sink writing the log message to one file
|
||||
*
|
||||
* The log sink writes all log messages to one single
|
||||
* file identified by a given path.
|
||||
*
|
||||
* Note because there is no limit, the file grows infinitely.
|
||||
*/
|
||||
class file_sink final : public basic_file_sink
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a file_sink with the given path.
|
||||
* If the path doesn't exist, it is created.
|
||||
*
|
||||
* @param path The log file to write to
|
||||
*/
|
||||
explicit file_sink(const std::string &path);
|
||||
|
||||
/**
|
||||
* Creates a file_sink with the given path.
|
||||
* If the path doesn't exist, it is created.
|
||||
*
|
||||
* @param path The log file to write to
|
||||
*/
|
||||
explicit file_sink(const char *path);
|
||||
|
||||
/**
|
||||
* Destroys the file_sink
|
||||
*/
|
||||
~file_sink() override;
|
||||
|
||||
/**
|
||||
* Returns the path to the log file.
|
||||
*
|
||||
* @return The path to the log file
|
||||
*/
|
||||
std::string path() const;
|
||||
|
||||
private:
|
||||
std::string path_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Log sink writing to stdout
|
||||
*
|
||||
* This log sink writes all messages to stdout.
|
||||
*/
|
||||
class stdout_sink final : public basic_file_sink
|
||||
{
|
||||
public:
|
||||
stdout_sink();
|
||||
~stdout_sink() override = default;
|
||||
|
||||
/**
|
||||
* Do nothing on close
|
||||
*/
|
||||
void close() override {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Log sink writing to stderr
|
||||
*
|
||||
* This log sink writes all messages to stderr.
|
||||
*/
|
||||
class stderr_sink final : public basic_file_sink
|
||||
{
|
||||
public:
|
||||
stderr_sink();
|
||||
~stderr_sink() override = default;
|
||||
|
||||
/**
|
||||
* Do nothing on close
|
||||
*/
|
||||
void close() override {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_FILE_SINK_HPP
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef MATADOR_LOG_DOMAIN_HPP
|
||||
#define MATADOR_LOG_DOMAIN_HPP
|
||||
|
||||
#include "matador/logger/log_level.hpp"
|
||||
#include "matador/logger/log_sink.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace matador::logger {
|
||||
|
||||
/**
|
||||
* @brief Connection to a set of log sinks
|
||||
*
|
||||
* A log domain is the connection point between
|
||||
* a set of log sinks and the logger objects
|
||||
* in the user code.
|
||||
*
|
||||
* A domain consists of a unique name and a
|
||||
* list of sinks
|
||||
*/
|
||||
class log_domain final
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* The time format for each log line
|
||||
*/
|
||||
static constexpr auto TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%f";
|
||||
|
||||
/**
|
||||
* Creates a log_domain with the given name
|
||||
* and the given log range
|
||||
*
|
||||
* @param name The name of the log domain
|
||||
* @param log_range The log range of this domain
|
||||
*/
|
||||
log_domain(std::string name, log_level_range log_range);
|
||||
|
||||
/**
|
||||
* Returns the name of the domain
|
||||
*
|
||||
* @return The name of the domain
|
||||
*/
|
||||
[[nodiscard]] std::string name() const;
|
||||
|
||||
/**
|
||||
* Sets the max log level. The default
|
||||
* max level is LVL_FATAL
|
||||
*
|
||||
* @param max_level max log level
|
||||
*/
|
||||
void max_log_level(log_level max_level);
|
||||
|
||||
/**
|
||||
* Returns the max log level
|
||||
*
|
||||
* @return The max log level
|
||||
*/
|
||||
[[nodiscard]] log_level max_log_level() const;
|
||||
|
||||
/**
|
||||
* Sets the min log level. Default
|
||||
* min leven is LVL_INFO
|
||||
*
|
||||
* @param min_level min log level
|
||||
*/
|
||||
void min_log_level(log_level min_level);
|
||||
|
||||
/**
|
||||
* Returns the min log level
|
||||
*
|
||||
* @return The min log level
|
||||
*/
|
||||
[[nodiscard]] log_level min_log_level() const;
|
||||
|
||||
/**
|
||||
* Add a sink to the domain.
|
||||
*
|
||||
* The sink must be packed into a std::shared_ptr
|
||||
* because it can be shared among other domains
|
||||
*
|
||||
* @param sink The sink to add
|
||||
*/
|
||||
void add_sink(sink_ptr sink);
|
||||
|
||||
/**
|
||||
* Logs the given message for the given source and log level
|
||||
* to this log domain.
|
||||
*
|
||||
* @param lvl Log level
|
||||
* @param source Source of the log message
|
||||
* @param message Message to log
|
||||
*/
|
||||
void log(log_level lvl, const std::string &source, const char *message) const;
|
||||
|
||||
/**
|
||||
* Clears the list of log sinks
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
static void get_time_stamp(char* timestamp_buffer);
|
||||
|
||||
private:
|
||||
static std::map<log_level, std::string> level_strings;
|
||||
|
||||
std::string name_;
|
||||
std::list<sink_ptr> sinks{};
|
||||
|
||||
log_level_range log_level_range_;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_LOG_DOMAIN_HPP
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef MATADOR_LOG_LEVEL_HPP
|
||||
#define MATADOR_LOG_LEVEL_HPP
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
namespace matador::logger {
|
||||
|
||||
/**
|
||||
* Represents all available log levels
|
||||
*/
|
||||
enum class log_level {
|
||||
Fatal, /**< If a serious error occurred, use FATAL level */
|
||||
Error, /**< On error use ERROR level */
|
||||
Warn, /**< Warnings should use WARN level */
|
||||
Info, /**< Information should go with INFO level */
|
||||
Debug, /**< Debug output should use DEBUG level */
|
||||
Trace, /**< Trace information should use TRACE level */
|
||||
All /**< This level represents all log levels and should be used for logging */
|
||||
};
|
||||
|
||||
/**
|
||||
* Write log level in a human-readable string
|
||||
* to a given std::ostream.
|
||||
*
|
||||
* @param os std::stream to write to
|
||||
* @param lvl Log level to write
|
||||
* @return The std::ostream
|
||||
*/
|
||||
std::ostream& operator<<(std::ostream &os, log_level lvl);
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
|
||||
struct log_level_range {
|
||||
log_level min_level = log_level::Fatal;
|
||||
log_level max_level = log_level::Info;
|
||||
};
|
||||
|
||||
/// @endcond
|
||||
|
||||
}
|
||||
#endif //MATADOR_LOG_LEVEL_HPP
|
||||
@@ -0,0 +1,297 @@
|
||||
#ifndef MATADOR_LOG_MANAGER_HPP
|
||||
#define MATADOR_LOG_MANAGER_HPP
|
||||
|
||||
#include "matador/utils/singleton.hpp"
|
||||
|
||||
#include "matador/logger/logger.hpp"
|
||||
#include "matador/logger/log_sink.hpp"
|
||||
#include "matador/logger/file_sink.hpp"
|
||||
#include "matador/logger/rotating_file_sink.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::logger {
|
||||
|
||||
/**
|
||||
* @brief Manages all log domains
|
||||
*
|
||||
* The log_manager class is a singleton and
|
||||
* manages all available log_domains
|
||||
*
|
||||
* There ist always a default log domain
|
||||
* with the name "default"
|
||||
* available for which sinks can be added
|
||||
* and loggers can be created.
|
||||
*/
|
||||
class log_manager final : public utils::singleton<log_manager>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a logger with the given source name
|
||||
* for the default log domain
|
||||
*
|
||||
* @param source Name of the source
|
||||
* @return The created logger
|
||||
*/
|
||||
[[nodiscard]] logger create_logger(std::string source) const;
|
||||
|
||||
/**
|
||||
* Creates a logger with the given source name
|
||||
* for the log domain identified by the given
|
||||
* log domain name.
|
||||
*
|
||||
* If the log domain with the given name doesn't exist,
|
||||
* the domain is created
|
||||
*
|
||||
* @param source Name of the source
|
||||
* @param domain_name The name of the log domain to execute to
|
||||
* @return The created logger
|
||||
*/
|
||||
logger create_logger(std::string source, const std::string &domain_name);
|
||||
|
||||
/**
|
||||
* Adds a log sink to the default log_domain
|
||||
*
|
||||
* @param sink Sink to add to the default log_domain
|
||||
*/
|
||||
void add_sink(sink_ptr sink) const;
|
||||
|
||||
/**
|
||||
* Adds a log sink to the log_domain with the given name.
|
||||
* If the log domain doesn't exist, it is automatically created.
|
||||
*
|
||||
* @param sink Sink to add
|
||||
* @param domain_name Name of the log domain
|
||||
*/
|
||||
void add_sink(sink_ptr sink, const std::string &domain_name);
|
||||
|
||||
/**
|
||||
* Clears all sinks from the default log domain
|
||||
*/
|
||||
void clear_all_sinks() const;
|
||||
|
||||
/**
|
||||
* Clears all sinks from the log domain
|
||||
* with the given name
|
||||
*
|
||||
* @param domain_name Domain name to clear all sinks from
|
||||
*/
|
||||
void clear_all_sinks(const std::string &domain_name);
|
||||
|
||||
/**
|
||||
* Remove all log domains but the default log domain.
|
||||
* Clears all sinks from the default log domain.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Sets the max default log level. The default
|
||||
* max leven is LVL_FATAL. All log domains
|
||||
* will start with this default max log range
|
||||
*
|
||||
* @param max_level max log level
|
||||
*/
|
||||
static void max_default_log_level(log_level max_level);
|
||||
|
||||
/**
|
||||
* Returns the default max log level
|
||||
*
|
||||
* @return The max log level
|
||||
*/
|
||||
static log_level max_default_log_level();
|
||||
|
||||
/**
|
||||
* Sets the default min log level. The default
|
||||
* min leven is LVL_INFO. All log domains
|
||||
* will start with this default max log range
|
||||
*
|
||||
* @param min_level min log level
|
||||
*/
|
||||
static void min_default_log_level(log_level min_level);
|
||||
|
||||
/**
|
||||
* Returns the default min log level
|
||||
*
|
||||
* @return The min log level
|
||||
*/
|
||||
static log_level min_default_log_level();
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
std::shared_ptr<log_domain> find_domain(const std::string &name);
|
||||
void log_default(log_level lvl, const std::string &source, const char *message) const;
|
||||
/// @endcond
|
||||
|
||||
protected:
|
||||
/// @cond MATADOR_DEV
|
||||
log_manager();
|
||||
/// @endcond
|
||||
|
||||
private:
|
||||
std::shared_ptr<log_domain> acquire_domain(const std::string &name);
|
||||
|
||||
private:
|
||||
friend class utils::singleton<log_manager>;
|
||||
|
||||
std::shared_ptr<log_domain> default_log_domain_;
|
||||
|
||||
std::map<std::string, std::shared_ptr<log_domain>> log_domain_map_;
|
||||
|
||||
static log_level_range default_log_level_range_;
|
||||
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut to create a file log sink
|
||||
* with the given path. If the path doesn't
|
||||
* exist, it is created.
|
||||
*
|
||||
* @param logfile Path to the logfile
|
||||
* @return A shared_ptr to the file_sink
|
||||
*/
|
||||
std::shared_ptr<file_sink> create_file_sink(const std::string &logfile);
|
||||
|
||||
/**
|
||||
* Shortcut to create a stderr log sink.
|
||||
*
|
||||
* @return A shared_ptr to the stderr_sink
|
||||
*/
|
||||
std::shared_ptr<stderr_sink> create_stderr_sink();
|
||||
|
||||
/**
|
||||
* Shortcut to create a stdout log sink.
|
||||
*
|
||||
* @return A shared_ptr to the stdout_sink
|
||||
*/
|
||||
std::shared_ptr<stdout_sink> create_stdout_sink();
|
||||
|
||||
/**
|
||||
* Shortcut to create a rotating file log sink
|
||||
* with the given path, max log files and max
|
||||
* log file size. If the path doesn't
|
||||
* exist, it is created.
|
||||
*
|
||||
* @param logfile Path to the log file
|
||||
* @param max_size Max log file size
|
||||
* @param file_count Max number of log files
|
||||
* @return A shared_ptr to the rotating_file_sink
|
||||
*/
|
||||
std::shared_ptr<rotating_file_sink> create_rotating_file_sink(const std::string &logfile, size_t max_size, size_t file_count);
|
||||
|
||||
/**
|
||||
* Sets the default min log level.
|
||||
*
|
||||
* @param min_lvl Default min log level
|
||||
*/
|
||||
void default_min_log_level(log_level min_lvl);
|
||||
|
||||
/**
|
||||
* Sets the default max log level.
|
||||
*
|
||||
* @param max_lvl Default max log level
|
||||
*/
|
||||
void default_max_log_level(log_level max_lvl);
|
||||
|
||||
/**
|
||||
* Sets the domain min log level for the
|
||||
* domain with the given name.
|
||||
*
|
||||
* @param name Log domain name
|
||||
* @param min_lvl Default min log level
|
||||
*/
|
||||
void domain_min_log_level(const std::string &name, log_level min_lvl);
|
||||
|
||||
/**
|
||||
* Sets the default max log level for the
|
||||
* domain with the given name.
|
||||
*
|
||||
* @param name Log domain name
|
||||
* @param max_lvl Default max log level
|
||||
*/
|
||||
void domain_max_log_level(const std::string &name, log_level max_lvl);
|
||||
|
||||
/**
|
||||
* Adds a log sink to the default log domain
|
||||
*
|
||||
* @param sink The log sink to add
|
||||
*/
|
||||
void add_log_sink(sink_ptr sink);
|
||||
|
||||
/**
|
||||
* Adds a log sink to the log domain
|
||||
* with the given name. If the domain
|
||||
* doesn't exist, it is created.
|
||||
*
|
||||
* @param sink The log sink to add
|
||||
* @param domain The log domain name to add
|
||||
*/
|
||||
void add_log_sink(sink_ptr sink, const std::string &domain);
|
||||
|
||||
/**
|
||||
* Removes all sinks from the
|
||||
* default domain
|
||||
*/
|
||||
void clear_all_log_sinks();
|
||||
|
||||
/**
|
||||
* Removes all sinks from the log domain
|
||||
* with the given domain name
|
||||
*
|
||||
* @param domain Domain name to clear all sinks
|
||||
*/
|
||||
void clear_all_log_sinks(const std::string &domain);
|
||||
|
||||
/**
|
||||
* Creates a logger with the given source name
|
||||
* connected to the default log domain.
|
||||
*
|
||||
* @param source The name of the source
|
||||
* @return The logger instance
|
||||
*/
|
||||
logger create_logger(std::string source);
|
||||
|
||||
/**
|
||||
* Creates a logger with the given source name
|
||||
* connected to the log domain with the given
|
||||
* name. If the domain doesn't exist, it is created
|
||||
*
|
||||
* @param source The name of the source
|
||||
* @param domain The name of the log domain
|
||||
* @return The logger instance
|
||||
*/
|
||||
logger create_logger(std::string source, const std::string &domain);
|
||||
|
||||
/**
|
||||
* Logs the given message for the given source and log level
|
||||
* to the default log domain.
|
||||
*
|
||||
* @param lvl Log level
|
||||
* @param source Source of the log message
|
||||
* @param message Message to log
|
||||
*/
|
||||
void log_default(log_level lvl, const std::string &source, const char *message);
|
||||
|
||||
/**
|
||||
* Log the given message with source and log level
|
||||
* to the default domain. The message will be created
|
||||
* from the what-argument and the args while the preprocessed
|
||||
* message uses the printf style to add the arguments.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments
|
||||
* @param lvl Log level
|
||||
* @param source Source of the log message
|
||||
* @param what The printf style message
|
||||
* @param args The arguments for the message
|
||||
*/
|
||||
template<typename... ARGS>
|
||||
void log(const log_level lvl, const std::string &source, const char *what, ARGS const &... args) {
|
||||
char message_buffer[logger::buffer_size];
|
||||
|
||||
snprintf(message_buffer, logger::buffer_size, what, args...);
|
||||
|
||||
log_default(lvl, source, message_buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_LOG_MANAGER_HPP
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef MATADOR_LOG_SINK_HPP
|
||||
#define MATADOR_LOG_SINK_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::logger {
|
||||
|
||||
/**
|
||||
* @brief Base class for all log sinks
|
||||
*
|
||||
* This class must be the base class for all
|
||||
* log sinks and provides their interface
|
||||
*
|
||||
* The main interface is the write() interface
|
||||
* defining how the log message is written.
|
||||
*
|
||||
* The close() interface defines a way to close
|
||||
* the concrete log sink
|
||||
*/
|
||||
class log_sink
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Destroys the log sink
|
||||
*/
|
||||
virtual ~log_sink() = default;
|
||||
|
||||
/**
|
||||
* Writes the given log message with the given size
|
||||
* to the concrete sink
|
||||
*
|
||||
* @param message The message to log
|
||||
* @param size The size of the message
|
||||
*/
|
||||
virtual void write(const char *message, std::size_t size) = 0;
|
||||
|
||||
/**
|
||||
* Closes the log sink if necessary.
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
};
|
||||
|
||||
using sink_ptr = std::shared_ptr<log_sink>; /**< Shortcut to the log sink shared pointer */
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_LOG_SINK_HPP
|
||||
@@ -0,0 +1,261 @@
|
||||
#ifndef MATADOR_LOGGER_HPP
|
||||
#define MATADOR_LOGGER_HPP
|
||||
|
||||
#include "matador/logger/log_level.hpp"
|
||||
#include "matador/logger/log_domain.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace matador::logger {
|
||||
/**
|
||||
* @brief logger to write log messages to log domains
|
||||
*
|
||||
* This class is used to write log messages to a connected
|
||||
* log domain (@sa log_domain).
|
||||
* Everywhere a logger is needed, it can be instantiated with
|
||||
* @code
|
||||
* matador::create_logger(source name)
|
||||
* @endcode
|
||||
*
|
||||
* The interface provides methods to log to each relevant
|
||||
* log level (@sa log_level)
|
||||
*
|
||||
* The message format syntax is like the printf syntax.
|
||||
* If the message string contains placeholder (beginning with %)
|
||||
* an argument is expected to be part of the argument list of the
|
||||
* calling method.
|
||||
*
|
||||
* All log messages are written through the internal
|
||||
* log_domain object to the sinks.
|
||||
*/
|
||||
class logger final {
|
||||
public:
|
||||
static constexpr size_t buffer_size = 16384;
|
||||
|
||||
/**
|
||||
* Create a logger with a given source name connected
|
||||
* to the given log_domain
|
||||
*
|
||||
* @param source The name of the source
|
||||
* @param log_domain The log_domain containing the log sinks
|
||||
*/
|
||||
logger(std::string source, std::shared_ptr<log_domain> log_domain);
|
||||
|
||||
logger(const logger& l) = delete;
|
||||
logger(logger&& l) noexcept;
|
||||
logger& operator=(const logger& l) = delete;
|
||||
logger& operator=(logger&& l) noexcept;
|
||||
|
||||
/**
|
||||
* Writes a log message string with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void fatal(const std::string &what, ARGS const &... args) const { fatal(what.c_str(), args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void fatal(const char *what, ARGS const &... args) const { log(log_level::Fatal, what, args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message string with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void error(const std::string &what, ARGS const &... args) const { error(what.c_str(), args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void error(const char *what, ARGS const &... args) const { log(log_level::Error, what, args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message string with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void warn(const std::string &what, ARGS const &... args) const { warn(what.c_str(), args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void warn(const char *what, ARGS const &... args) const { log(log_level::Warn, what, args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message string with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void info(const std::string &what, ARGS const &... args) const { info(what.c_str(), args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void info(const char *what, ARGS const &... args) const { log(log_level::Info, what, args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message string with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void debug(const std::string &what, ARGS const &... args) const { debug(what.c_str(), args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void debug(const char *what, ARGS const &... args) const { log(log_level::Debug, what, args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message string with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void trace(const std::string &what, ARGS const &... args) const { trace(what.c_str(), args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||
* to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void trace(const char *what, ARGS const &... args) const { log(log_level::Trace, what, args...); }
|
||||
|
||||
/**
|
||||
* Writes a log message represented by a char pointer
|
||||
* with the given log level to the connected log_domain.
|
||||
*
|
||||
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||
* @param lvl The log level
|
||||
* @param what The message to log
|
||||
* @param args The arguments to be replaced in the message
|
||||
*/
|
||||
template<typename ... ARGS>
|
||||
void log(log_level lvl, const char *what, ARGS const &... args) const;
|
||||
|
||||
/**
|
||||
* Writes a log message represented by a char pointer
|
||||
* with the given log level to the connected log_domain.
|
||||
*
|
||||
* @param lvl The log level
|
||||
* @param what The message to log
|
||||
*/
|
||||
void log(log_level lvl, const char *what) const;
|
||||
|
||||
/**
|
||||
* Returns the name of the source the logger represents
|
||||
*
|
||||
* @return Represented log source name
|
||||
*/
|
||||
[[nodiscard]] const std::string& source() const;
|
||||
|
||||
/**
|
||||
* Returns the name of the connected log domain
|
||||
*
|
||||
* @return The name of the log domain
|
||||
*/
|
||||
[[nodiscard]] std::string domain() const;
|
||||
|
||||
private:
|
||||
std::string source_;
|
||||
std::shared_ptr<log_domain> logger_domain_;
|
||||
};
|
||||
|
||||
template<typename... ARGS>
|
||||
void logger::log(const log_level lvl, const char *what, ARGS const &... args) const {
|
||||
char message_buffer[buffer_size];
|
||||
|
||||
snprintf(message_buffer, buffer_size, what, args...);
|
||||
|
||||
logger_domain_->log(lvl, source_, message_buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
template<typename T>
|
||||
decltype(auto) myForward(T&& t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
template<>
|
||||
decltype(auto) myForward(std::string& t)
|
||||
{
|
||||
return t.c_str();
|
||||
}
|
||||
|
||||
template<>
|
||||
decltype(auto) myForward(std::string&& t)
|
||||
{
|
||||
return t.c_str();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static void log(const char* pszFmt, Args&&... args)
|
||||
{
|
||||
doSomething(pszFmt, myForward<Args>(std::forward<Args>(args))...);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
#endif //MATADOR_LOGGER_HPP
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef MATADOR_ROTATING_FILE_SINK_HPP
|
||||
#define MATADOR_ROTATING_FILE_SINK_HPP
|
||||
|
||||
#include "matador/logger/log_sink.hpp"
|
||||
#include "matador/utils/file.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::logger {
|
||||
|
||||
/**
|
||||
* @brief A rotating log file sink
|
||||
*
|
||||
* This log sink provides a possibility to
|
||||
* rotate several log files if the current
|
||||
* log file reaches the maximum size.
|
||||
*
|
||||
* The user can define the maximum number of log files
|
||||
* and the maximum size of the current log file
|
||||
*
|
||||
* The name of the current log file is defined within the
|
||||
* given logfile path. Each rotated (moved) log file gets
|
||||
* an incremented number extension right before the
|
||||
* file extension, e.g.:
|
||||
*
|
||||
* Log file name is 'log.txt' the first rotated log file
|
||||
* is named 'log-1.txt' and so on until the maximum
|
||||
* number of log files is reached. Then it starts from
|
||||
* the beginning.
|
||||
* Keep in mind that the log file to which is currently
|
||||
* written to is always named like the file name
|
||||
* given within the path.
|
||||
*/
|
||||
class rotating_file_sink final : public log_sink
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a rotating log file sink within the given path
|
||||
* with the given maximum number of rotating log files where
|
||||
* each file size is never greater than the given max file
|
||||
* size.
|
||||
*
|
||||
* @param path Path of the log file
|
||||
* @param max_size Max log file size
|
||||
* @param file_count Max log file count
|
||||
*/
|
||||
rotating_file_sink(const std::string& path, size_t max_size, size_t file_count);
|
||||
|
||||
/**
|
||||
* Write the message to the current log file. If the
|
||||
* actual size exceeds the file size limit, the log files
|
||||
* are rotated.
|
||||
*
|
||||
* @param message Message to write
|
||||
* @param size The size of the log message
|
||||
*/
|
||||
void write(const char *message, size_t size) override;
|
||||
|
||||
/**
|
||||
* Close all open log files
|
||||
*/
|
||||
void close() override;
|
||||
|
||||
private:
|
||||
std::string calculate_filename(size_t fileno);
|
||||
|
||||
void rotate();
|
||||
void prepare(const std::string &path);
|
||||
|
||||
private:
|
||||
file logfile_;
|
||||
std::string path_;
|
||||
std::string base_path_;
|
||||
std::string extension_;
|
||||
size_t max_size_ = 0;
|
||||
size_t current_size_ = 0;
|
||||
size_t current_file_no_ = 0;
|
||||
size_t file_count_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_ROTATING_FILE_SINK_HPP
|
||||
@@ -0,0 +1,174 @@
|
||||
#ifndef MATADOR_ACCEPTOR_HPP
|
||||
#define MATADOR_ACCEPTOR_HPP
|
||||
|
||||
#include "matador/net/export.hpp"
|
||||
#include "matador/net/handler.hpp"
|
||||
#include "matador/net/handler_creator.hpp"
|
||||
#include "matador/net/ip.hpp"
|
||||
|
||||
#include "matador/logger/logger.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace matador {
|
||||
|
||||
/**
|
||||
* The acceptor class is used to accept new connection
|
||||
* within the reactor dispatcher.
|
||||
*
|
||||
* Once a new connection was accepted by the acceptor a
|
||||
* new handler is created and registered within the reactor
|
||||
* to handle the established connection
|
||||
*/
|
||||
class OOS_NET_API acceptor : public handler, public handler_creator
|
||||
{
|
||||
public:
|
||||
typedef std::function<std::shared_ptr<handler>(tcp::socket sock, tcp::peer endpoint, acceptor *accptr)> t_accept_handler; /**< Shortcut to a function creating a handler on successfully accepted a new connection */
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
acceptor();
|
||||
|
||||
/**
|
||||
* Creates an acceptor with the given endpoint. The endpoint
|
||||
* represents the address on which the acceptor listens for new
|
||||
* connections
|
||||
*
|
||||
* @param endpoint Endpoint to listen for new connections
|
||||
*/
|
||||
explicit acceptor(tcp::peer endpoint);
|
||||
|
||||
/**
|
||||
* Creates an acceptor with the given endpoint. The endpoint
|
||||
* represents the address on which the acceptor listens for new
|
||||
* connections. The given function is called when a new
|
||||
* connection was accepted and returns a new handler for
|
||||
* the new connection.
|
||||
*
|
||||
* @param endpoint Endpoint to listen for new connections
|
||||
* @param on_new_connection Function creating a new handler for each accepted new connection
|
||||
*/
|
||||
acceptor(tcp::peer endpoint, t_accept_handler on_new_connection);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~acceptor() override;
|
||||
|
||||
/**
|
||||
* When a new connection is accepted the given function
|
||||
* is called to create a new handler for the connection
|
||||
*
|
||||
* @param on_new_connection Function creating a new handler for the new connection
|
||||
*/
|
||||
void accecpt(t_accept_handler on_new_connection);
|
||||
|
||||
/**
|
||||
* Accepts new connection at the given endpoint.
|
||||
* When a new connection is accepted the given function
|
||||
* is called to create a new handler for the connection.
|
||||
*
|
||||
* @param endpoint Endpoint to listen for new connections
|
||||
* @param on_new_connection Function creating a new handler for the new connection
|
||||
*/
|
||||
void accecpt(const tcp::peer& endpoint, t_accept_handler on_new_connection);
|
||||
|
||||
/**
|
||||
* Opens the acceptor means the socket address of the
|
||||
* endpoint is bound to the created listing socket
|
||||
* Then the socket is used for listening for new
|
||||
* connections.
|
||||
*/
|
||||
void open() override;
|
||||
|
||||
/**
|
||||
* Returns the current listening socket fd
|
||||
*
|
||||
* @return Listening socket fd
|
||||
*/
|
||||
socket_type handle() const override;
|
||||
|
||||
/**
|
||||
* Is called when a new connection wants to
|
||||
* execute to the endpoint. Once the connection was accepted
|
||||
* a new connection handler is created and the socket is
|
||||
* passed to the handler. The handler is then registered
|
||||
* to the reactor to disptach its read and write events.
|
||||
*/
|
||||
void on_input() override;
|
||||
|
||||
/**
|
||||
* Does actually nothing
|
||||
*/
|
||||
void on_output() override {}
|
||||
|
||||
/**
|
||||
* Does actually nothing
|
||||
*/
|
||||
void on_except() override {}
|
||||
|
||||
/**
|
||||
* Does actually nothing
|
||||
*/
|
||||
void on_timeout() override {}
|
||||
|
||||
/**
|
||||
* Does actually nothing
|
||||
*/
|
||||
void on_close() override {}
|
||||
|
||||
/**
|
||||
* Closes the listen fd of the acceptor
|
||||
*/
|
||||
void close() override;
|
||||
|
||||
/**
|
||||
* Returns always false because new connections
|
||||
* are indicated as read events.
|
||||
*
|
||||
* @return Always false
|
||||
*/
|
||||
bool is_ready_write() const override;
|
||||
|
||||
/**
|
||||
* Returns true if the acceptor was opened
|
||||
* and a listen fd was created.
|
||||
*
|
||||
* @return True If a listen socket was created
|
||||
*/
|
||||
bool is_ready_read() const override;
|
||||
|
||||
/**
|
||||
* Notifies the acceptor that
|
||||
* this handler was closed.
|
||||
*
|
||||
* @param hndlr Closed handler.
|
||||
*/
|
||||
void notify_close(handler *hndlr) override;
|
||||
|
||||
std::string name() const override;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns the current endpoint accepting new connection.
|
||||
*
|
||||
* @return Current listening endpoint
|
||||
*/
|
||||
const tcp::peer& endpoint() const;
|
||||
|
||||
private:
|
||||
tcp::acceptor acceptor_;
|
||||
tcp::peer endpoint_;
|
||||
|
||||
std::string name_ { "acceptor" };
|
||||
|
||||
t_accept_handler accept_handler_;
|
||||
|
||||
logger log_;
|
||||
|
||||
tcp::peer create_client_endpoint() const;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_ACCEPTOR_HPP
|
||||
@@ -0,0 +1,453 @@
|
||||
#ifndef MATADOR_ADDRESS_HPP
|
||||
#define MATADOR_ADDRESS_HPP
|
||||
|
||||
#include "matador/utils/os.hpp"
|
||||
|
||||
#include "matador/net/export.hpp"
|
||||
#include "matador/net/os.hpp"
|
||||
#include "matador/net/error.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
namespace matador {
|
||||
|
||||
/**
|
||||
* Enum representing the protocol
|
||||
* family IPv4 and IPv6
|
||||
*/
|
||||
enum protocol_family {
|
||||
V4, /**< IPv4 enum value */
|
||||
V6 /**< IPv6 enum value */
|
||||
};
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
template < protocol_family PF >
|
||||
class address_router;
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* The address class represents a IPv4 or
|
||||
* IPv6 address.
|
||||
*/
|
||||
class OOS_NET_API address
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
address() = default;
|
||||
|
||||
/**
|
||||
* Constructs an address from the given
|
||||
* addr representing a IPv4 socket address
|
||||
* structure
|
||||
*
|
||||
* @param addr Initial IPv4 Socket address
|
||||
*/
|
||||
explicit address(const sockaddr_in &addr);
|
||||
|
||||
/**
|
||||
* Constructs an address from the given
|
||||
* addr representing a IPv6 socket address
|
||||
* structure
|
||||
*
|
||||
* @param addr Initial IPv6 Socket address
|
||||
*/
|
||||
explicit address(const sockaddr_in6 &addr);
|
||||
|
||||
/**
|
||||
* Copy constructs an address from the
|
||||
* given address x
|
||||
*
|
||||
* @param x Address to copy from
|
||||
*/
|
||||
address(const address &x) = default;
|
||||
|
||||
/**
|
||||
* Copy assign an address from the
|
||||
* given address x
|
||||
*
|
||||
* @param x Address to assign from
|
||||
* @return The assigned address
|
||||
*/
|
||||
address& operator=(const address &x);
|
||||
|
||||
/**
|
||||
* Move copy constructs an address from the
|
||||
* given address x
|
||||
*
|
||||
* @param x Address to move copy from
|
||||
*/
|
||||
address(address &&x) noexcept;
|
||||
|
||||
/**
|
||||
* Move assign an address from the
|
||||
* given address x
|
||||
*
|
||||
* @param x Address to move assign from
|
||||
* @return The moved address
|
||||
*/
|
||||
address& operator=(address &&x) noexcept;
|
||||
|
||||
/**
|
||||
* Destructs the address
|
||||
*/
|
||||
~address();
|
||||
|
||||
/**
|
||||
* Returns the address as unsigned long value
|
||||
*
|
||||
* @return The address as unsigned long value
|
||||
*/
|
||||
unsigned int to_ulong() const;
|
||||
|
||||
/**
|
||||
* Return the address in string format either
|
||||
* in IPv4 dotted format or in IPv6 colon based
|
||||
* format
|
||||
*
|
||||
* @return The address as string
|
||||
*/
|
||||
std::string to_string() const;
|
||||
|
||||
/**
|
||||
* Sets the port number
|
||||
*
|
||||
* @param pn Port number to set
|
||||
*/
|
||||
void port(unsigned short pn);
|
||||
|
||||
/**
|
||||
* Returns the current port number of the
|
||||
* address.
|
||||
*
|
||||
* @return The current port number
|
||||
*/
|
||||
unsigned short port() const;
|
||||
|
||||
/**
|
||||
* Returns true if address is IPv4
|
||||
*
|
||||
* @return True if IPv4 address
|
||||
*/
|
||||
bool is_v4() const;
|
||||
|
||||
/**
|
||||
* Returns true if address is IPv4
|
||||
*
|
||||
* @return True if IPv4 address
|
||||
*/
|
||||
bool is_v6() const;
|
||||
|
||||
/**
|
||||
* Returns the raw sockaddr structure
|
||||
*
|
||||
* @return The raw sockaddr structure
|
||||
*/
|
||||
sockaddr* addr();
|
||||
|
||||
/**
|
||||
* Returns the raw sockaddr structure
|
||||
*
|
||||
* @return The raw sockaddr structure
|
||||
*/
|
||||
const sockaddr* addr() const;
|
||||
|
||||
/**
|
||||
* Returns the IPv4 sockaddr_in structure
|
||||
*
|
||||
* @return The IPv4 sockaddr_in structure
|
||||
*/
|
||||
sockaddr_in* addr_v4();
|
||||
|
||||
/**
|
||||
* Returns the IPv4 sockaddr_in structure
|
||||
*
|
||||
* @return The IPv4 sockaddr_in structure
|
||||
*/
|
||||
const sockaddr_in* addr_v4() const;
|
||||
|
||||
/**
|
||||
* Returns the IPv6 sockaddr_in6 structure
|
||||
*
|
||||
* @return The IPv6 sockaddr_in6 structure
|
||||
*/
|
||||
sockaddr_in6* addr_v6();
|
||||
|
||||
/**
|
||||
* Returns the IPv6 sockaddr_in6 structure
|
||||
*
|
||||
* @return The IPv6 sockaddr_in6 structure
|
||||
*/
|
||||
const sockaddr_in6* addr_v6() const;
|
||||
|
||||
/**
|
||||
* Returns the size of the underlying
|
||||
* address structure.
|
||||
*
|
||||
* @return Size of the underlying address structure
|
||||
*/
|
||||
socklen_t size() const;
|
||||
|
||||
typedef address_router<V4> v4; /**< Shortcut to the internal IPv4 address router */
|
||||
typedef address_router<V6> v6; /**< Shortcut to the internal IPv6 address router */
|
||||
|
||||
private:
|
||||
void clear();
|
||||
|
||||
private:
|
||||
template < protocol_family PF >
|
||||
friend class address_router;
|
||||
|
||||
union socket_address {
|
||||
sockaddr sa_raw;
|
||||
sockaddr_in sa_in;
|
||||
sockaddr_in6 sa_in6;
|
||||
};
|
||||
|
||||
socket_address socket_address_ {};
|
||||
|
||||
socklen_t size_ = 0;
|
||||
};
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
template <>
|
||||
class address_router<V4>
|
||||
{
|
||||
public:
|
||||
address_router() = delete;
|
||||
address_router& operator=(const address_router&) = delete;
|
||||
address_router(const address_router&) = delete;
|
||||
address_router& operator=(address_router&&) = delete;
|
||||
address_router(address_router&&) = delete;
|
||||
|
||||
static address empty() { return mk_address(INADDR_ANY); };
|
||||
static address any() { return mk_address(INADDR_ANY); }
|
||||
static address loopback() { return mk_address(INADDR_LOOPBACK); }
|
||||
static address broadcast() {return mk_address(INADDR_BROADCAST); }
|
||||
static address from_ip(const std::string &str) { return from_ip(str.c_str()); }
|
||||
static address from_ip(const char *str)
|
||||
{
|
||||
// now fill in the address info struct
|
||||
// create and fill the hints struct
|
||||
if (str == nullptr) {
|
||||
return address();
|
||||
}
|
||||
// get address from string
|
||||
|
||||
sockaddr_in addr{};
|
||||
int ret = os::inet_pton(PF_INET, str, &(addr.sin_addr));
|
||||
if (ret == 1) {
|
||||
addr.sin_family = PF_INET;
|
||||
return address(addr);
|
||||
} else if (ret == 0) {
|
||||
detail::throw_logic_error("invalid ip address");
|
||||
} else {
|
||||
detail::throw_logic_error_with_errno("invalid ip address: %s", errno);
|
||||
}
|
||||
return address();
|
||||
}
|
||||
|
||||
static address from_hostname(const std::string &str) { return from_hostname(str.c_str()); }
|
||||
static address from_hostname(const char *str)
|
||||
{
|
||||
// now fill in the address info struct
|
||||
// create and fill the hints struct
|
||||
if (str == nullptr) {
|
||||
return address();
|
||||
}
|
||||
// get address from string
|
||||
sockaddr_in addr{};
|
||||
|
||||
int ret;
|
||||
|
||||
if ((ret = os::inet_pton(AF_INET, str, &addr.sin_addr)) == 1) {
|
||||
addr.sin_family = PF_INET;
|
||||
} else if (ret == -1) {
|
||||
detail::throw_logic_error_with_errno("invalid address: %s", errno);
|
||||
} else { /* 0 == try name */
|
||||
struct addrinfo hints{};
|
||||
struct addrinfo *result = nullptr;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_INET; /* Allow IPv4 or IPv6 */
|
||||
hints.ai_socktype = SOCK_STREAM; /* Stream socket */
|
||||
hints.ai_flags = AI_PASSIVE; /* Numeric or net network hostname */
|
||||
hints.ai_protocol = 0; /* Any protocol */
|
||||
hints.ai_canonname = nullptr;
|
||||
hints.ai_addr = nullptr;
|
||||
hints.ai_next = nullptr;
|
||||
|
||||
int s = getaddrinfo(str, nullptr, &hints, &result);
|
||||
if (s != 0) {
|
||||
detail::throw_logic_error_with_gai_errno("invalid ip address (getaddrinfo): %s", s);
|
||||
}
|
||||
|
||||
/* getaddrinfo() returns a list of address structures.
|
||||
Try each address until we successfully bind(2).
|
||||
If socket(2) (or bind(2)) fails, we (close the socket
|
||||
and) try the next address. */
|
||||
|
||||
// take first address
|
||||
memcpy(&addr, result->ai_addr, sizeof(&result->ai_addr));
|
||||
|
||||
freeaddrinfo(result); /* No longer needed */
|
||||
}
|
||||
addr.sin_family = PF_INET;
|
||||
memset(addr.sin_zero, '\0', sizeof(addr.sin_zero));
|
||||
return address(addr);
|
||||
}
|
||||
|
||||
private:
|
||||
static address mk_address(unsigned int inaddr)
|
||||
{
|
||||
sockaddr_in addr{};
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = PF_INET;
|
||||
addr.sin_addr.s_addr = htonl(inaddr);
|
||||
return address(addr);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class address_router<V6>
|
||||
{
|
||||
public:
|
||||
address_router() = delete;
|
||||
address_router& operator=(const address_router&) = delete;
|
||||
address_router(const address_router&) = delete;
|
||||
address_router& operator=(address_router&&) = delete;
|
||||
address_router(address_router&&) = delete;
|
||||
|
||||
static address empty() { return mk_address(in6addr_any); }
|
||||
static address any() { return mk_address(in6addr_any); }
|
||||
static address loopback() { return mk_address(in6addr_loopback); }
|
||||
static address broadcast() {return mk_multicast_address(); }
|
||||
|
||||
static address from_ip(const std::string &str)
|
||||
{
|
||||
return from_ip(str.c_str());
|
||||
}
|
||||
|
||||
static address from_ip(const char *str)
|
||||
{
|
||||
// now fill in the address info struct
|
||||
// create and fill the hints struct
|
||||
if (str == nullptr) {
|
||||
return address();
|
||||
}
|
||||
// get address from string
|
||||
|
||||
sockaddr_in6 addr{};
|
||||
int ret = os::inet_pton(PF_INET6, str, &(addr.sin6_addr));
|
||||
if (ret == 1) {
|
||||
addr.sin6_family = PF_INET6;
|
||||
return address(addr);
|
||||
} else if (ret == 0) {
|
||||
char message_buffer[1024];
|
||||
os::sprintf(message_buffer, 1024, "INET_PTON (ip): invalid ip address [%s]", str);
|
||||
detail::throw_logic_error(message_buffer);
|
||||
} else {
|
||||
char message_buffer[1024];
|
||||
int err = errno;
|
||||
os::sprintf(message_buffer, 1024, "INET_PTON (ip): invalid ip address [%s]: %%s", str);
|
||||
detail::throw_logic_error_with_errno(message_buffer, err);
|
||||
}
|
||||
return address();
|
||||
}
|
||||
|
||||
static address from_hostname(const std::string &str)
|
||||
{
|
||||
return from_hostname(str.c_str());
|
||||
}
|
||||
|
||||
static address from_hostname(const char *str)
|
||||
{
|
||||
// now fill in the address info struct
|
||||
// create and fill the hints struct
|
||||
if (str == nullptr) {
|
||||
return address();
|
||||
}
|
||||
// get address from string
|
||||
sockaddr_in6 addr{};
|
||||
|
||||
int ret = os::inet_pton(PF_INET6, str, &addr.sin6_addr);
|
||||
|
||||
if (ret == 1) {
|
||||
addr.sin6_family = PF_INET6;
|
||||
} else if (ret == -1) {
|
||||
char message_buffer[1024];
|
||||
int err = errno;
|
||||
os::sprintf(message_buffer, 1024, "INET_PTON (host): invalid ip address [%s] (errno: %d): %%s", str, err);
|
||||
detail::throw_logic_error_with_errno(message_buffer, err);
|
||||
} else { /* 0 == try name */
|
||||
struct addrinfo hints{};
|
||||
struct addrinfo *result = nullptr;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_INET6; /* Allow IPv4 or IPv6 */
|
||||
hints.ai_socktype = SOCK_STREAM; /* Stream socket */
|
||||
hints.ai_flags = AI_PASSIVE; /* Numeric or net network hostname */
|
||||
hints.ai_protocol = 0; /* Any protocol */
|
||||
hints.ai_canonname = nullptr;
|
||||
hints.ai_addr = nullptr;
|
||||
hints.ai_next = nullptr;
|
||||
|
||||
int s = getaddrinfo(str, nullptr, &hints, &result);
|
||||
if (s != 0) {
|
||||
char message_buffer[1024];
|
||||
os::sprintf(message_buffer, 1024, "GETADDRINFO (host): invalid ip address [%s] (errno: %d): %%s", str, s);
|
||||
detail::throw_logic_error_with_errno(message_buffer, s);
|
||||
}
|
||||
|
||||
/* getaddrinfo() returns a list of address structures.
|
||||
Try each address until we successfully bind(2).
|
||||
If socket(2) (or bind(2)) fails, we (close the socket
|
||||
and) try the next address. */
|
||||
|
||||
// take first address
|
||||
memcpy(&addr, result->ai_addr, result->ai_addrlen);
|
||||
|
||||
freeaddrinfo(result); /* No longer needed */
|
||||
}
|
||||
addr.sin6_family = PF_INET6;
|
||||
return address(addr);
|
||||
}
|
||||
|
||||
private:
|
||||
static OOS_NET_API const char *IP6ADDR_MULTICAST_ALLNODES;
|
||||
|
||||
static address mk_address(in6_addr in6addr)
|
||||
{
|
||||
sockaddr_in6 addr{};
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin6_family = PF_INET6;
|
||||
addr.sin6_addr = in6addr;
|
||||
return address(addr);
|
||||
}
|
||||
|
||||
static address mk_multicast_address()
|
||||
{
|
||||
sockaddr_in6 addr{};
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin6_family = PF_INET6;
|
||||
os::inet_pton(AF_INET6, IP6ADDR_MULTICAST_ALLNODES, &addr.sin6_addr);
|
||||
return address(addr);
|
||||
}
|
||||
};
|
||||
|
||||
/// @endcond
|
||||
|
||||
}
|
||||
#endif //MATADOR_ADDRESS_HPP
|
||||
@@ -0,0 +1,115 @@
|
||||
#ifndef MATADOR_ADDRESS_RESOLVER_HPP
|
||||
#define MATADOR_ADDRESS_RESOLVER_HPP
|
||||
|
||||
#include "matador/net/export.hpp"
|
||||
#include "matador/net/peer.hpp"
|
||||
#include "matador/net/error.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace matador {
|
||||
/// @cond MATADOR_DEV
|
||||
class tcp;
|
||||
class udp;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template < class P >
|
||||
int determine_socktype();
|
||||
|
||||
template <>
|
||||
OOS_NET_API int determine_socktype<tcp>();
|
||||
|
||||
template <>
|
||||
OOS_NET_API int determine_socktype<udp>();
|
||||
|
||||
template < class P >
|
||||
void initialize_hints(struct addrinfo &hints, int flags) {
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = determine_socktype<P>();
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_flags = flags;
|
||||
}
|
||||
/// @endcond
|
||||
}
|
||||
|
||||
/**
|
||||
* The address resolver resolves a given host and port
|
||||
* to a peer object representing the given address
|
||||
*
|
||||
* @tparam P Type of protocol
|
||||
*/
|
||||
template < class P >
|
||||
class address_resolver
|
||||
{
|
||||
public:
|
||||
typedef typename P::peer peer; /**< Shortcut to peer type */
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
address_resolver() = default;
|
||||
|
||||
/**
|
||||
* Resolves the given host and port to a list
|
||||
* of valid peers representing the ip addresses
|
||||
* of the host either in IPv4 or IPv6 format
|
||||
*
|
||||
* @param hostname Hostname to resolve
|
||||
* @param port Port to resolve
|
||||
* @return A list of peers representing the host and port
|
||||
*/
|
||||
std::vector<peer> resolve(const std::string &hostname, const std::string &port);
|
||||
|
||||
/**
|
||||
* Resolves the given host and port to a list
|
||||
* of valid peers representing the ip addresses
|
||||
* of the host either in IPv4 or IPv6 format
|
||||
*
|
||||
* @param hostname Hostname to resolve
|
||||
* @param port Port to resolve
|
||||
* @return A list of peers representing the host and port
|
||||
*/
|
||||
std::vector<peer> resolve(const char *hostname, const char *port);
|
||||
|
||||
};
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
template<class P>
|
||||
std::vector<typename address_resolver<P>::peer> address_resolver<P>::resolve(const std::string &hostname, const std::string &port)
|
||||
{
|
||||
return resolve(hostname.c_str(), port.c_str());
|
||||
}
|
||||
|
||||
template < class P >
|
||||
std::vector<typename address_resolver<P>::peer> address_resolver<P>::resolve(const char *hostname, const char *port)
|
||||
{
|
||||
struct addrinfo hints = {};
|
||||
detail::initialize_hints<P>(hints, AI_PASSIVE);
|
||||
struct addrinfo* res = nullptr;
|
||||
struct addrinfo* head = nullptr;
|
||||
int err = getaddrinfo(hostname, port, &hints, &res);
|
||||
|
||||
if (err != 0) {
|
||||
detail::throw_logic_error_with_gai_errno("error on getaddrinfo: %s", err);
|
||||
}
|
||||
|
||||
head = res;
|
||||
std::vector<peer> peers;
|
||||
do {
|
||||
|
||||
if (res->ai_family == PF_INET) {
|
||||
peers.push_back(peer(address(*(struct sockaddr_in*)res->ai_addr)));
|
||||
} else if (res->ai_family == PF_INET6) {
|
||||
peers.push_back(peer(address(*(struct sockaddr_in6*)res->ai_addr)));
|
||||
} // else -> not supported
|
||||
} while ( (res = res->ai_next) != nullptr);
|
||||
|
||||
freeaddrinfo(head);
|
||||
|
||||
return peers;
|
||||
}
|
||||
/// @endcond
|
||||
}
|
||||
#endif //MATADOR_ADDRESS_RESOLVER_HPP
|
||||
@@ -0,0 +1,146 @@
|
||||
#ifndef MATADOR_CONNECTOR_HPP
|
||||
#define MATADOR_CONNECTOR_HPP
|
||||
|
||||
#include "matador/net/export.hpp"
|
||||
#include "matador/net/handler.hpp"
|
||||
#include "matador/net/handler_creator.hpp"
|
||||
#include "matador/net/ip.hpp"
|
||||
|
||||
#include "matador/logger/logger.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace matador {
|
||||
|
||||
/**
|
||||
* Connector which initiates a reactor based connection
|
||||
* to a remote network host identified by a list
|
||||
* of endpoints.
|
||||
*
|
||||
* Once a connection is established a handler is created
|
||||
* with the given create handler function. The socket
|
||||
* is passed to the created handler.
|
||||
*/
|
||||
class OOS_NET_API connector : public handler, public handler_creator
|
||||
{
|
||||
public:
|
||||
typedef std::function<std::shared_ptr<handler>(tcp::socket sock, tcp::peer endpoint, connector *cnnctr)> t_connect_handler; /**< Shortcut to a function creating a handler on successfully execute to a host */
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
connector();
|
||||
|
||||
/**
|
||||
* Creates a new connector with the given
|
||||
* create handler function
|
||||
*
|
||||
* @param on_new_connection Function which creates a handler on new connection
|
||||
*/
|
||||
explicit connector(t_connect_handler on_new_connection);
|
||||
|
||||
|
||||
/**
|
||||
* Initiates a execute to one of the given endpoints within the
|
||||
* given reactor. Once a connection is established a new handler
|
||||
* for this connection is created. The new connection is dispatched
|
||||
* by the reactor.
|
||||
*
|
||||
* @param r Reactor to handle the connector and the created connection
|
||||
* @param endpoints List of endpoints to
|
||||
*/
|
||||
void connect(reactor &r, const std::vector<tcp::peer> &endpoints);
|
||||
/**
|
||||
* Initiates a execute to one of the given endpoints within the
|
||||
* given reactor. Once a connection is established a new handler
|
||||
* for this connection is created with the given function. The new
|
||||
* connection is dispatched by the reactor.
|
||||
*
|
||||
* @param r Reactor to handle the connector and the created connection
|
||||
* @param endpoints List of endpoints to
|
||||
* @param on_new_connection Function creating a new handler on new connection
|
||||
*/
|
||||
void connect(reactor &r, const std::vector<tcp::peer> &endpoints, t_connect_handler on_new_connection);
|
||||
|
||||
/**
|
||||
* Opens the connector. Actually this does
|
||||
* nothing at all.
|
||||
*/
|
||||
void open() override {}
|
||||
|
||||
/**
|
||||
* Returns the handle of the connector. The result will
|
||||
* always be zero, because the connector doesn't has a socket
|
||||
*
|
||||
* @return Always zero
|
||||
*/
|
||||
socket_type handle() const override;
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
*/
|
||||
void on_input() override {}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
*/
|
||||
void on_output() override {}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
*/
|
||||
void on_except() override {}
|
||||
|
||||
/**
|
||||
* The timeout call is used to establish the connection.
|
||||
* Once the connection could be established within this call
|
||||
* the timeout is canceled otherwise it will be tried again
|
||||
* after three seconds
|
||||
*/
|
||||
void on_timeout() override;
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
*/
|
||||
void on_close() override {}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
*/
|
||||
void close() override {}
|
||||
|
||||
/**
|
||||
* Always false because connection is established via timeout
|
||||
*
|
||||
* @return Always false
|
||||
*/
|
||||
bool is_ready_write() const override;
|
||||
|
||||
/**
|
||||
* Always false because connection is established via timeout
|
||||
*
|
||||
* @return Always false
|
||||
*/
|
||||
bool is_ready_read() const override;
|
||||
|
||||
/**
|
||||
* Notifies the connector that
|
||||
* this handler was closed.
|
||||
*
|
||||
* @param hndlr Closed handler.
|
||||
*/
|
||||
void notify_close(handler *hndlr) override;
|
||||
|
||||
std::string name() const override;
|
||||
|
||||
private:
|
||||
t_connect_handler connect_handler_;
|
||||
|
||||
logger log_;
|
||||
|
||||
std::vector<tcp::peer> endpoints_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_CONNECTOR_HPP
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifndef MATADOR_EVENT_TYPE_HPP
|
||||
#define MATADOR_EVENT_TYPE_HPP
|
||||
|
||||
namespace matador::net {
|
||||
|
||||
/**
|
||||
* Enum representing an event processing mask
|
||||
* for the reactor io dispatcher. The mask values
|
||||
* can be bitwise combined and are used to
|
||||
* check if a handler (@see handler.hpp) is able
|
||||
* to read, write or accept.
|
||||
*/
|
||||
enum class event_type : unsigned {
|
||||
NONE_MASK = 0, /**< Enum value for no event */
|
||||
READ_MASK = 1 << 0, /**< Enum value for read mask */
|
||||
WRITE_MASK = 1 << 1, /**< Enum value for write mask */
|
||||
EXCEPT_MASK = 1 << 2, /**< Enum value for except mask */
|
||||
TIMEOUT_MASK = 1 << 3, /**< Enum value for timeout mask */
|
||||
ACCEPT_MASK = READ_MASK, /**< Enum value for accept mask */
|
||||
READ_WRITE_MASK = READ_MASK | WRITE_MASK, /**< Enum value for read write mask */
|
||||
ALL_MASK = READ_MASK | WRITE_MASK | EXCEPT_MASK | ACCEPT_MASK /**< Enum value for all events mask */
|
||||
};
|
||||
|
||||
/**
|
||||
* event_type operator to concat to event types
|
||||
* with or operator
|
||||
*
|
||||
* @param a Left event_type
|
||||
* @param b Right event_type
|
||||
* @return The combined result
|
||||
*/
|
||||
inline event_type operator|(event_type a, event_type b)
|
||||
{
|
||||
return static_cast<event_type>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* event_type operator to concat to event types
|
||||
* with and operator
|
||||
*
|
||||
* @param a Left event_type
|
||||
* @param b Right event_type
|
||||
* @return The combined result
|
||||
*/
|
||||
inline event_type operator&(event_type a, event_type b)
|
||||
{
|
||||
return static_cast<event_type>(static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* event_type not operator
|
||||
*
|
||||
* @param a Left event_type
|
||||
* @return The combined result
|
||||
*/
|
||||
inline event_type operator~ (event_type a)
|
||||
{
|
||||
return (event_type)~(int)a;
|
||||
}
|
||||
|
||||
/**
|
||||
* event_type operator to concat to event types
|
||||
* with xor operator
|
||||
*
|
||||
* @param a Left event_type
|
||||
* @param b Right event_type
|
||||
* @return The combined result
|
||||
*/
|
||||
inline event_type operator^ (event_type a, event_type b)
|
||||
{
|
||||
return (event_type)((int)a ^ (int)b);
|
||||
}
|
||||
|
||||
/**
|
||||
* event_type operator to assign event type
|
||||
* with or operator
|
||||
*
|
||||
* @param a Left event_type
|
||||
* @param b Right event_type
|
||||
* @return The combined result
|
||||
*/
|
||||
inline event_type& operator|= (event_type& a, event_type b)
|
||||
{
|
||||
return (event_type&)((int&)a |= (int)b);
|
||||
}
|
||||
|
||||
/**
|
||||
* event_type operator to assign event type
|
||||
* with and operator
|
||||
*
|
||||
* @param a Left event_type
|
||||
* @param b Right event_type
|
||||
* @return The combined result
|
||||
*/
|
||||
inline event_type& operator&= (event_type& a, event_type b)
|
||||
{
|
||||
return (event_type&)((int&)a &= (int)b);
|
||||
}
|
||||
|
||||
/**
|
||||
* event_type operator to assign event type
|
||||
* with xor operator
|
||||
*
|
||||
* @param a Left event_type
|
||||
* @param b Right event_type
|
||||
* @return The combined result
|
||||
*/
|
||||
inline event_type& operator^= (event_type& a, event_type b)
|
||||
{
|
||||
return (event_type&)((int&)a ^= (int)b);
|
||||
}
|
||||
/**
|
||||
* Checks if a specific event_type is set in
|
||||
* a given event type mask. If type is set
|
||||
* true is returned.
|
||||
*
|
||||
* @param source Event type mask to check.
|
||||
* @param needle Requested event type
|
||||
* @return True if event type is set
|
||||
*/
|
||||
inline bool is_event_type_set(event_type source, event_type needle)
|
||||
{
|
||||
return static_cast<int>(source & needle) > 0;
|
||||
}
|
||||
|
||||
}
|
||||
#endif //MATADOR_EVENT_TYPE_HPP
|
||||
@@ -0,0 +1,111 @@
|
||||
#ifndef MATADOR_FDSET_HPP
|
||||
#define MATADOR_FDSET_HPP
|
||||
|
||||
#include "matador/net/export.hpp"
|
||||
#include "matador/net/os.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <functional>
|
||||
#include <cstddef>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
namespace matador {
|
||||
|
||||
/**
|
||||
* This class represents a fd set
|
||||
* used by the reactor class. There it
|
||||
* is used in combination with a call
|
||||
* to select.
|
||||
*/
|
||||
class OOS_NET_API fdset
|
||||
{
|
||||
public:
|
||||
fdset(const fdset&) = delete;
|
||||
fdset& operator=(const fdset&) = delete;
|
||||
|
||||
/**
|
||||
* Default constructor creates
|
||||
* an empty fd set
|
||||
*/
|
||||
fdset();
|
||||
|
||||
/**
|
||||
* Destroys the fd set
|
||||
*/
|
||||
~fdset() = default;
|
||||
|
||||
fdset(fdset&& x) noexcept;
|
||||
fdset& operator=(fdset&& x) noexcept;
|
||||
|
||||
/**
|
||||
* Reset all bits to zero
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Checks if the given fd is set
|
||||
* in the fd set.
|
||||
*
|
||||
* @param fd Requested fd
|
||||
* @return True if fd is set
|
||||
*/
|
||||
bool is_set(socket_type fd) const;
|
||||
|
||||
/**
|
||||
* Clears the giveb fd from the set.
|
||||
*
|
||||
* @param fd fd to clear
|
||||
*/
|
||||
void clear(socket_type fd);
|
||||
|
||||
/**
|
||||
* Sets the given fd in the fd set.
|
||||
*
|
||||
* @param fd fd to set
|
||||
*/
|
||||
void set(socket_type fd);
|
||||
|
||||
/**
|
||||
* Returns the highest fd plus one.
|
||||
* This is needed for the call to select
|
||||
*
|
||||
* @return Highest fd plus one
|
||||
*/
|
||||
socket_type maxp1() const;
|
||||
|
||||
/**
|
||||
* Returns the current number of fd in the set
|
||||
*
|
||||
* @return Number of fd in set
|
||||
*/
|
||||
size_t count() const;
|
||||
|
||||
/**
|
||||
* Checks if the set is empty
|
||||
*
|
||||
* @return True if the fd set is empty
|
||||
*/
|
||||
bool empty() const;
|
||||
|
||||
/**
|
||||
* Returns a pointer to the underlying fd_set structure
|
||||
*
|
||||
* @return Pointer to the underlying fd_set structure
|
||||
*/
|
||||
fd_set* get();
|
||||
|
||||
private:
|
||||
typedef std::set<socket_type, std::greater<> > int_set_t;
|
||||
int_set_t max_fd_set_;
|
||||
|
||||
fd_set fd_set_ = {};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_FDSET_HPP
|
||||
@@ -0,0 +1,138 @@
|
||||
#ifndef MATADOR_HANDLER_HPP
|
||||
#define MATADOR_HANDLER_HPP
|
||||
|
||||
#include "matador/net/os.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace matador::net {
|
||||
|
||||
class reactor;
|
||||
|
||||
/**
|
||||
* Base class for all handlers used
|
||||
* with the reactor. The handler must implement its
|
||||
* interface to handle input, output, exceptional and
|
||||
* timeout data.
|
||||
*/
|
||||
class handler : public std::enable_shared_from_this<handler>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Virtual destructor
|
||||
*/
|
||||
virtual ~handler() = default;
|
||||
|
||||
/**
|
||||
* Interface to open a handler. This
|
||||
* is called when a handler is registered
|
||||
* within the reactor
|
||||
*/
|
||||
virtual void open() = 0;
|
||||
|
||||
/**
|
||||
* Interface to returns the socket handle
|
||||
* of the concrete handler implementation.
|
||||
*
|
||||
* @return The socket fd
|
||||
*/
|
||||
virtual socket_type handle() const = 0;
|
||||
|
||||
/**
|
||||
* Interface handling incoming data
|
||||
*/
|
||||
virtual void on_input() = 0;
|
||||
|
||||
/**
|
||||
* Interface handling outgoing data
|
||||
*/
|
||||
virtual void on_output() = 0;
|
||||
|
||||
/**
|
||||
* Interface handling exceptional data
|
||||
*/
|
||||
virtual void on_except() = 0;
|
||||
|
||||
/**
|
||||
* Interface handling timout data
|
||||
*/
|
||||
virtual void on_timeout() = 0;
|
||||
|
||||
/**
|
||||
* Interface called when the handler is closed
|
||||
*/
|
||||
virtual void on_close() = 0;
|
||||
|
||||
/**
|
||||
* Interface implementation should close
|
||||
* the handle gracefully
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
/**
|
||||
* Interface should return true if there
|
||||
* is outgoing data
|
||||
*
|
||||
* @return True if there is outgoing data
|
||||
*/
|
||||
virtual bool is_ready_write() const = 0;
|
||||
|
||||
/**
|
||||
* Interface should return true if there
|
||||
* is incoming data
|
||||
*
|
||||
* @return True if there is incoming data
|
||||
*/
|
||||
virtual bool is_ready_read() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the next timeout scheduled
|
||||
* in the reactor
|
||||
*
|
||||
* @return Next timeout
|
||||
*/
|
||||
time_t next_timeout() const;
|
||||
|
||||
/**
|
||||
* Returns the timeout interval in
|
||||
* seconds
|
||||
*
|
||||
* @return Timeout interval
|
||||
*/
|
||||
time_t interval() const;
|
||||
|
||||
/**
|
||||
* Get the name of the handler.
|
||||
* The name don't need to be unique.
|
||||
* It has only an informational purpose.
|
||||
*
|
||||
* @return The name of the handler
|
||||
*/
|
||||
virtual std::string name() const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Gets the underlying reactor
|
||||
*
|
||||
* @return The underlying reactor
|
||||
*/
|
||||
reactor* get_reactor() const;
|
||||
|
||||
private:
|
||||
friend class reactor;
|
||||
|
||||
void register_reactor(reactor *r);
|
||||
void schedule(time_t offset, time_t interval);
|
||||
void cancel_timer();
|
||||
void calculate_next_timeout(time_t now);
|
||||
|
||||
reactor *reactor_ = nullptr;
|
||||
|
||||
time_t next_timeout_ = 0;
|
||||
time_t interval_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_HANDLER_HPP
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef MATADOR_HANDLER_CREATOR_HPP
|
||||
#define MATADOR_HANDLER_CREATOR_HPP
|
||||
|
||||
#include "matador/net/export.hpp"
|
||||
|
||||
namespace matador {
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
|
||||
class handler;
|
||||
|
||||
class OOS_NET_API handler_creator
|
||||
{
|
||||
public:
|
||||
virtual ~handler_creator() = default;
|
||||
|
||||
virtual void notify_close(handler *hndlr) = 0;
|
||||
};
|
||||
|
||||
/// @endcond
|
||||
|
||||
}
|
||||
#endif //MATADOR_HANDLER_CREATOR_HPP
|
||||
@@ -0,0 +1,128 @@
|
||||
#ifndef MATADOR_IO_SERVICE_HPP
|
||||
#define MATADOR_IO_SERVICE_HPP
|
||||
|
||||
#include "matador/utils/buffer.hpp"
|
||||
|
||||
#include "matador/net/export.hpp"
|
||||
#include "matador/net/reactor.hpp"
|
||||
#include "matador/net/acceptor.hpp"
|
||||
#include "matador/net/connector.hpp"
|
||||
#include "matador/net/stream_handler.hpp"
|
||||
|
||||
namespace matador {
|
||||
|
||||
/**
|
||||
* IO Service is used to encapsulate the an instance
|
||||
* of the reactor class.
|
||||
*/
|
||||
class OOS_NET_API io_service
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a io_service
|
||||
*/
|
||||
io_service();
|
||||
|
||||
~io_service();
|
||||
/**
|
||||
* Starts the io_service with the underlying reactor
|
||||
*/
|
||||
void run();
|
||||
|
||||
/**
|
||||
* Returns true if the io service is
|
||||
* running
|
||||
*
|
||||
* @return True if service is running
|
||||
*/
|
||||
bool is_running() const;
|
||||
|
||||
/**
|
||||
* Shuts down a running service
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Adds the given acceptor for the
|
||||
* given peer endpoint and callback.
|
||||
*
|
||||
* The callback is called, when a new connection
|
||||
* was accepted.
|
||||
*
|
||||
* @tparam AcceptCallback Type of callback
|
||||
* @param ac Acceptor used to accept connections
|
||||
* @param ep Endpoint on which the acceptor will listen
|
||||
* @param accept_callback Callback when connection was accepted
|
||||
*/
|
||||
template < typename AcceptCallback >
|
||||
void accept(const std::shared_ptr<acceptor>& ac, const tcp::peer &ep, AcceptCallback accept_callback);
|
||||
|
||||
/**
|
||||
* Adds the given acceptor for the
|
||||
* given callback.
|
||||
*
|
||||
* The callback is called, when a new connection
|
||||
* was accepted.
|
||||
*
|
||||
* @tparam AcceptCallback Type of callback
|
||||
* @param ac Acceptor used to accept connections
|
||||
* @param accept_callback Callback when connection was accepted
|
||||
*/
|
||||
template < typename AcceptCallback >
|
||||
void accept(const std::shared_ptr<acceptor>& ac, AcceptCallback accept_callback);
|
||||
|
||||
/**
|
||||
* Add the given connector for the given port
|
||||
* and execute callback.
|
||||
*
|
||||
* Once a connection is established the callback
|
||||
* is called.
|
||||
*
|
||||
* @tparam ConnectCallback Type of the callback
|
||||
* @param co Connector Used to establish the connection
|
||||
* @param port Port to execute to
|
||||
* @param connect_callback Callback when connection was established
|
||||
*/
|
||||
template < typename ConnectCallback >
|
||||
void connect(const std::shared_ptr<connector>& co, const std::string &port, ConnectCallback connect_callback);
|
||||
|
||||
private:
|
||||
logger log_;
|
||||
reactor reactor_;
|
||||
};
|
||||
|
||||
template<typename AcceptCallback>
|
||||
void io_service::accept(const std::shared_ptr<acceptor>& ac, const tcp::peer &ep, AcceptCallback accept_callback)
|
||||
{
|
||||
log_.info("registering acceptor for %s", ep.to_string().c_str());
|
||||
ac->accecpt(ep, [accept_callback, this](tcp::socket sock, tcp::peer p, acceptor *accptr) {
|
||||
return std::make_shared<stream_handler>(sock, p, accptr, accept_callback);
|
||||
});
|
||||
|
||||
reactor_.register_handler(ac, event_type::ACCEPT_MASK);
|
||||
}
|
||||
|
||||
template<typename AcceptCallback>
|
||||
void io_service::accept(const std::shared_ptr<acceptor>& ac, AcceptCallback accept_callback)
|
||||
{
|
||||
log_.info("registering acceptor for %s", ac->endpoint().to_string().c_str());
|
||||
ac->accecpt([accept_callback](tcp::socket sock, tcp::peer p, acceptor *accptr) {
|
||||
return std::make_shared<stream_handler>(sock, p, accptr, accept_callback);
|
||||
});
|
||||
|
||||
reactor_.register_handler(ac, event_type::ACCEPT_MASK);
|
||||
}
|
||||
|
||||
template<typename ConnectCallback>
|
||||
void io_service::connect(const std::shared_ptr<connector> &co, const std::string &port, ConnectCallback connect_callback)
|
||||
{
|
||||
log_.info("registering connector for localhost:%s", port.c_str());
|
||||
tcp::resolver resolver;
|
||||
auto endpoints = resolver.resolve("localhost", port);
|
||||
co->connect(reactor_, endpoints, [connect_callback](const tcp::socket& sock, const tcp::peer &p, connector *cnnctr) {
|
||||
return std::make_shared<stream_handler>(sock, p, cnnctr, connect_callback);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
#endif //MATADOR_IO_SERVICE_HPP
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef MATADOR_IO_STREAM_HPP
|
||||
#define MATADOR_IO_STREAM_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
|
||||
#include "matador/net/export.hpp"
|
||||
#include "matador/net/ip.hpp"
|
||||
|
||||
namespace matador {
|
||||
|
||||
class buffer;
|
||||
class buffer_view;
|
||||
|
||||
/**
|
||||
* The io stream class is proposed
|
||||
* to be used with the io_service class
|
||||
* and provides therefore an interface
|
||||
* which is used by the io_service
|
||||
*/
|
||||
class OOS_NET_API io_stream
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(int ec, long nread)> t_read_handler; /**< Short for function to process read data */
|
||||
typedef std::function<void(int ec, long nwrite)> t_write_handler; /**< Short for function to prepare data to write */
|
||||
|
||||
/**
|
||||
* This interface is called when data should
|
||||
* be read from a socket. Once the date was read
|
||||
* the given read handler is called.
|
||||
*
|
||||
* @param buf Buffer to read the data in
|
||||
* @param read_handler Handler to be called when data was read
|
||||
*/
|
||||
virtual void read(buffer_view buf, t_read_handler read_handler) = 0;
|
||||
|
||||
/**
|
||||
* This interface is called when data should be written
|
||||
* to a socket. Once the data was written the given
|
||||
* write handler is called.
|
||||
*
|
||||
* @param buffers List of buffers containing the data to write
|
||||
* @param write_handler Handler to be called when the data was written
|
||||
*/
|
||||
virtual void write(std::list<buffer_view> buffers, t_write_handler write_handler) = 0;
|
||||
|
||||
/**
|
||||
* Closes the stream
|
||||
*/
|
||||
virtual void close_stream() = 0;
|
||||
|
||||
/**
|
||||
* Returns the underlying stream socket
|
||||
* @return
|
||||
*/
|
||||
virtual tcp::socket& stream() = 0;
|
||||
|
||||
/**
|
||||
* Returns a name for the io stream.
|
||||
*
|
||||
* @return Name of the io stream
|
||||
*/
|
||||
virtual std::string name() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_IO_STREAM_HPP
|
||||
@@ -0,0 +1,130 @@
|
||||
#ifndef MATADOR_IP_HPP
|
||||
#define MATADOR_IP_HPP
|
||||
|
||||
#include "matador/net/peer.hpp"
|
||||
#include "matador/net/address_resolver.hpp"
|
||||
#include "matador/net/socket.hpp"
|
||||
#include "matador/net/socket_stream.hpp"
|
||||
#include "matador/net/socket_acceptor.hpp"
|
||||
|
||||
namespace matador::net {
|
||||
|
||||
/**
|
||||
* The tcp class represents all
|
||||
* settings to handle tcp socket
|
||||
* connections
|
||||
*/
|
||||
class tcp {
|
||||
public:
|
||||
typedef peer_base<tcp> peer; /**< Shortcut to a tcp-based peer */
|
||||
typedef socket_stream<tcp> socket; /**< Shortcut to a tcp-based socket */
|
||||
typedef socket_acceptor<tcp> acceptor; /**< Shortcut to a tcp-based acceptor */
|
||||
typedef address_resolver<tcp> resolver; /**< Shortcut to a tcp-based address resolver */
|
||||
|
||||
/**
|
||||
* Returns the type of the socket
|
||||
*
|
||||
* @return Type of the socket
|
||||
*/
|
||||
static int type() { return SOCK_STREAM; }
|
||||
|
||||
/**
|
||||
* Returns the socket protocol
|
||||
*
|
||||
* @return Socket protocol
|
||||
*/
|
||||
static int protocol() { return IPPROTO_TCP; }
|
||||
|
||||
/**
|
||||
* Returns the socket family
|
||||
*
|
||||
* @return Socket family
|
||||
*/
|
||||
int family() const { return family_; }
|
||||
|
||||
/**
|
||||
* Creates an instance of tcp representing
|
||||
* an IPv4 socket
|
||||
*
|
||||
* @return IPv4 tcp object
|
||||
*/
|
||||
static tcp v4() { return tcp(PF_INET); }
|
||||
|
||||
/**
|
||||
* Creates an instance of tcp representing
|
||||
* an IPv6 socket
|
||||
*
|
||||
* @return IPv6 tcp object
|
||||
*/
|
||||
static tcp v6() { return tcp(PF_INET6); }
|
||||
|
||||
private:
|
||||
explicit tcp(const int family)
|
||||
: family_(family)
|
||||
{}
|
||||
|
||||
int family_;
|
||||
};
|
||||
|
||||
/**
|
||||
* The udp class represents all
|
||||
* settings to handle udp socket
|
||||
* connections
|
||||
*/
|
||||
class OOS_NET_API udp
|
||||
{
|
||||
public:
|
||||
typedef peer_base<udp> peer; /**< Shortcut to an udp-based peer */
|
||||
typedef socket_stream<udp> socket; /**< Shortcut to an udp-based socket */
|
||||
typedef socket_acceptor<udp> acceptor; /**< Shortcut to an udp-based acceptor */
|
||||
typedef address_resolver<udp> resolver; /**< Shortcut to an udp-based address resolver */
|
||||
|
||||
/**
|
||||
* Returns the type of the socket
|
||||
*
|
||||
* @return Type of the socket
|
||||
*/
|
||||
static int type() { return SOCK_DGRAM; }
|
||||
|
||||
/**
|
||||
* Returns the socket protocol
|
||||
*
|
||||
* @return Socket protocol
|
||||
*/
|
||||
static int protocol() { return IPPROTO_UDP; }
|
||||
|
||||
/**
|
||||
* Returns the socket family
|
||||
*
|
||||
* @return Socket family
|
||||
*/
|
||||
int family() const { return family_; }
|
||||
|
||||
/**
|
||||
* Creates an instance of udp representing
|
||||
* an IPv4 socket
|
||||
*
|
||||
* @return IPv4 udp object
|
||||
*/
|
||||
static udp v4() { return udp(PF_INET); }
|
||||
|
||||
/**
|
||||
* Creates an instance of udp representing
|
||||
* an IPv6 socket
|
||||
*
|
||||
* @return IPv6 udp object
|
||||
*/
|
||||
static udp v6() { return udp(PF_INET6); }
|
||||
|
||||
private:
|
||||
explicit udp(const int family)
|
||||
: family_(family)
|
||||
{}
|
||||
|
||||
int family_;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_IP_HPP
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef MATADOR_NET_OS_HPP
|
||||
#define MATADOR_NET_OS_HPP
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#if _WIN32
|
||||
#include <WinSock2.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
using socket_type = SOCKET;
|
||||
#else
|
||||
using socket_type = int;
|
||||
#endif
|
||||
|
||||
namespace matador {
|
||||
namespace net {
|
||||
|
||||
OOS_NET_API void init();
|
||||
OOS_NET_API void cleanup();
|
||||
|
||||
}
|
||||
|
||||
bool is_valid_socket(socket_type fd);
|
||||
|
||||
namespace os {
|
||||
|
||||
OOS_NET_API int inet_pton(int af, const char *src, void *dst);
|
||||
OOS_NET_API const char* inet_ntop(int af, const void* src, char* dst, size_t size);
|
||||
|
||||
enum class shutdown_type
|
||||
{
|
||||
READ = 0,
|
||||
WRITE = 1,
|
||||
READ_WRITE = 2
|
||||
};
|
||||
|
||||
OOS_NET_API int shutdown(socket_type fd, shutdown_type type);
|
||||
OOS_NET_API int close(socket_type fd);
|
||||
|
||||
}
|
||||
}
|
||||
#endif //MATADOR_NET_OS_HPP
|
||||
@@ -0,0 +1,187 @@
|
||||
#ifndef MATADOR_PEER_HPP
|
||||
#define MATADOR_PEER_HPP
|
||||
|
||||
#include "matador/net/address.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
//#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace matador::net {
|
||||
|
||||
/**
|
||||
* The peer_base class acts like the holder
|
||||
* of network endpoint information like socket
|
||||
* address and port. The template argument
|
||||
* sets the protocol type either TCP or UDP.
|
||||
*
|
||||
* @tparam P Protocol type
|
||||
*/
|
||||
template < class P >
|
||||
class peer_base
|
||||
{
|
||||
public:
|
||||
typedef P protocol_type; /**< Short to a protocol-type */
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
peer_base() = default;
|
||||
|
||||
/**
|
||||
* Creates a peer from a given address. Port is set
|
||||
* to zero.
|
||||
*
|
||||
* @param addr Address to create the peer from
|
||||
*/
|
||||
explicit peer_base(address addr)
|
||||
: addr_(std::move(addr))
|
||||
{}
|
||||
|
||||
/**
|
||||
* Creates a peer from a given address and port
|
||||
*
|
||||
* @param addr Address to create the peer from
|
||||
* @param port Port of the endpoint
|
||||
*/
|
||||
peer_base(address addr, const unsigned short port)
|
||||
: addr_(std::move(addr)) {
|
||||
addr_.port(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy creates a peer from the given peer
|
||||
*
|
||||
* @param x Peer to copy from
|
||||
*/
|
||||
peer_base(const peer_base &x)
|
||||
: addr_(x.addr_) {}
|
||||
|
||||
/**
|
||||
* Move creates a peer from a given peer
|
||||
*
|
||||
* @param x Peer to move from
|
||||
*/
|
||||
peer_base(peer_base &&x) noexcept
|
||||
: addr_(std::move(x.addr_)) {}
|
||||
|
||||
/**
|
||||
* Copy assigns a given peer to this peer
|
||||
*
|
||||
* @param x Peer to assign
|
||||
* @return The assigned peer
|
||||
*/
|
||||
peer_base& operator=(const peer_base &x) {
|
||||
addr_ = x.addr_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign moves the given peer to this peer
|
||||
*
|
||||
* @param x The peer to move assign
|
||||
* @return The moved peer
|
||||
*/
|
||||
peer_base& operator=(peer_base &&x) noexcept {
|
||||
addr_ = std::move(x.addr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~peer_base() = default;
|
||||
|
||||
/**
|
||||
* Returns the current port of the peer.
|
||||
*
|
||||
* @return The current port
|
||||
*/
|
||||
int port() const { return addr_.port(); }
|
||||
|
||||
/**
|
||||
* Returns the current IP protocol of the peer
|
||||
* address which is either IPv4 or IPv6
|
||||
*
|
||||
* @return The current IP protocol
|
||||
*/
|
||||
protocol_type protocol() const {
|
||||
return addr_.is_v4() ? protocol_type::v4() : protocol_type::v6();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw pointer to the sockaddr structure
|
||||
*
|
||||
* @return The raw pointer to the sockaddr structure
|
||||
*/
|
||||
sockaddr* data() {
|
||||
return addr_.addr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw pointer to the sockaddr structure
|
||||
*
|
||||
* @return The raw pointer to the sockaddr structure
|
||||
*/
|
||||
const sockaddr* data() const {
|
||||
return addr_.addr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the underlying sockaddr structure
|
||||
*
|
||||
* @return The size of the underlying sockaddr structure
|
||||
*/
|
||||
size_t size() const {
|
||||
return addr_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the address
|
||||
*
|
||||
* @return A reference to the address
|
||||
*/
|
||||
address& addr() {
|
||||
return addr_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the address
|
||||
*
|
||||
* @return A reference to the address
|
||||
*/
|
||||
const address& addr() const {
|
||||
return addr_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the peer endpoint to a string in
|
||||
* the format [ip]:[port]
|
||||
*
|
||||
* @return Returns a string representation of the peer
|
||||
*/
|
||||
std::string to_string() const {
|
||||
char address_str[INET6_ADDRSTRLEN + 8];
|
||||
const char *name;
|
||||
if (addr().is_v4()) {
|
||||
name = os::inet_ntop(addr_.addr()->sa_family, &addr_.addr_v4()->sin_addr, address_str, INET6_ADDRSTRLEN);
|
||||
} else {
|
||||
name = os::inet_ntop(addr_.addr()->sa_family, &addr_.addr_v6()->sin6_addr, address_str, INET6_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
const size_t pos = strlen(name);
|
||||
|
||||
snprintf(address_str + pos, INET6_ADDRSTRLEN+8-pos, ":%d", addr_.port());
|
||||
return address_str;
|
||||
}
|
||||
|
||||
private:
|
||||
address addr_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_PEER_HPP
|
||||
@@ -0,0 +1,119 @@
|
||||
#ifndef REACTOR_HPP
|
||||
#define REACTOR_HPP
|
||||
|
||||
#include "matador/net/event_type.hpp"
|
||||
#include "matador/net/select_fd_sets.hpp"
|
||||
#include "matador/net/socket_interrupter.hpp"
|
||||
|
||||
#include "matador/utils/leader_follower_thread_pool.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
#include <queue>
|
||||
#include <list>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace matador::net {
|
||||
|
||||
class handler;
|
||||
|
||||
class reactor {
|
||||
public:
|
||||
using handler_ptr = std::shared_ptr<handler>;
|
||||
using handler_weak_ptr = std::weak_ptr<handler>;
|
||||
|
||||
struct Statistics {
|
||||
std::atomic<size_t> total_events_handled_{0};
|
||||
std::atomic<size_t> read_events_{0};
|
||||
std::atomic<size_t> write_events_{0};
|
||||
std::atomic<size_t> timer_events_{0};
|
||||
std::atomic<size_t> errors_{0};
|
||||
};
|
||||
|
||||
struct HandlerEntry {
|
||||
explicit HandlerEntry(handler_ptr h, event_type et)
|
||||
: handler(std::move(h)), events(et) {}
|
||||
|
||||
handler_ptr handler;
|
||||
event_type events;
|
||||
time_t next_timeout{0};
|
||||
time_t interval{0};
|
||||
bool marked_for_deletion{false};
|
||||
};
|
||||
|
||||
reactor();
|
||||
~reactor();
|
||||
|
||||
reactor(const reactor&) = delete;
|
||||
reactor& operator=(const reactor&) = delete;
|
||||
|
||||
void register_handler(const handler_ptr& h, event_type type);
|
||||
void unregister_handler(const handler_ptr& h, event_type type);
|
||||
void schedule_timer(const handler_ptr& h, time_t offset, time_t interval);
|
||||
void cancel_timer(const handler_ptr& h);
|
||||
void run();
|
||||
void handle_events();
|
||||
void shutdown();
|
||||
[[nodiscard]] bool is_running() const { return running_; }
|
||||
select_fdsets fd_sets() const;
|
||||
void mark_handler_for_delete(const handler_ptr& h);
|
||||
void activate_handler(const handler_ptr& h, event_type ev);
|
||||
void deactivate_handler(const handler_ptr& h, event_type ev);
|
||||
const Statistics& get_statistics() const { return stats_; }
|
||||
|
||||
private:
|
||||
struct TimerEvent {
|
||||
time_t timeout{};
|
||||
handler_weak_ptr handler;
|
||||
|
||||
bool operator>(const TimerEvent& other) const {
|
||||
return timeout > other.timeout;
|
||||
}
|
||||
};
|
||||
|
||||
using handler_map = std::unordered_map<handler*, std::unique_ptr<HandlerEntry>>;
|
||||
using timer_queue = std::priority_queue<TimerEvent, std::vector<TimerEvent>, std::greater<>>;
|
||||
|
||||
void prepare_select_bits(time_t& timeout, select_fdsets& fd_sets) const;
|
||||
bool process_events(const select_fdsets& fd_sets, time_t now);
|
||||
void process_timers(time_t now);
|
||||
void cleanup_deleted_handlers();
|
||||
void handle_error(const std::string& operation, const handler_ptr& h);
|
||||
int perform_select(struct timeval* timeout, select_fdsets& fd_sets);
|
||||
bool check_interruption(const select_fdsets& fd_sets);
|
||||
|
||||
void safe_handler_operation(const handler_ptr& h, const std::string& op_name,
|
||||
const std::function<void()>& operation);
|
||||
|
||||
HandlerEntry* find_handler_entry(const handler_ptr& h);
|
||||
void remove_handler_entry(const handler_ptr& h);
|
||||
static void update_handler_events(HandlerEntry* entry, event_type ev, bool activate);
|
||||
|
||||
private:
|
||||
handler_map handlers_;
|
||||
timer_queue timers_;
|
||||
|
||||
std::atomic<bool> running_{false};
|
||||
std::atomic<bool> shutdown_requested_{false};
|
||||
|
||||
mutable std::shared_mutex handlers_mutex_;
|
||||
std::mutex timers_mutex_;
|
||||
std::condition_variable_any shutdown_cv_;
|
||||
// std::condition_variable shutdown_cv_;
|
||||
|
||||
logger::logger log_;
|
||||
Statistics stats_;
|
||||
|
||||
utils::leader_follower_thread_pool thread_pool_;
|
||||
socket_interrupter interrupter_;
|
||||
|
||||
static constexpr std::chrono::seconds CLEANUP_INTERVAL{60};
|
||||
time_t last_cleanup_{0};
|
||||
};
|
||||
|
||||
} // namespace matador
|
||||
|
||||
#endif //REACTOR_HPP
|
||||
@@ -0,0 +1,132 @@
|
||||
#ifndef MATADOR_SELECT_FDSETS_HPP
|
||||
#define MATADOR_SELECT_FDSETS_HPP
|
||||
|
||||
#include "matador/net/fdset.hpp"
|
||||
|
||||
namespace matador::net {
|
||||
|
||||
/**
|
||||
* This class represents three fd sets
|
||||
* needed for the system call to select.
|
||||
*/
|
||||
class select_fdsets
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* These enum values are used
|
||||
* to identify and access the three
|
||||
* different fd sets
|
||||
*/
|
||||
typedef enum {
|
||||
read_type = 0, /**< Enum value for the read fd set */
|
||||
write_type = 1, /**< Enum value for the write fd set */
|
||||
except_type /**< Enum value for the exceptional fd set */
|
||||
} fdset_type;
|
||||
|
||||
/**
|
||||
* Returns the highest fd value over
|
||||
* all three fd sets plus one. This is used
|
||||
* by the system call to select.
|
||||
*
|
||||
* @return Highest fd value over all sets
|
||||
*/
|
||||
socket_type maxp1() const;
|
||||
|
||||
/**
|
||||
* Returns the fdset identified by the given type
|
||||
*
|
||||
* @param type Requested fdset type
|
||||
* @return The requested fd set
|
||||
*/
|
||||
fdset& fd_set(fdset_type type);
|
||||
|
||||
/**
|
||||
* Returns the read fd set.
|
||||
*
|
||||
* @return The read fd set.
|
||||
*/
|
||||
fdset& read_set();
|
||||
|
||||
/**
|
||||
* Returns the read fd set.
|
||||
*
|
||||
* @return The read fd set.
|
||||
*/
|
||||
const fdset& read_set() const;
|
||||
|
||||
/**
|
||||
* Returns the write fd set.
|
||||
*
|
||||
* @return The write fd set.
|
||||
*/
|
||||
fdset& write_set();
|
||||
|
||||
/**
|
||||
* Returns the write fd set.
|
||||
*
|
||||
* @return The write fd set.
|
||||
*/
|
||||
const fdset& write_set() const;
|
||||
|
||||
/**
|
||||
* Returns the exceptional fd set.
|
||||
*
|
||||
* @return The exceptional fd set.
|
||||
*/
|
||||
fdset& except_set();
|
||||
|
||||
/**
|
||||
* Returns the exceptional fd set.
|
||||
*
|
||||
* @return The exceptional fd set.
|
||||
*/
|
||||
const fdset& except_set() const;
|
||||
|
||||
/**
|
||||
* Reset all bits in all three sets to zero
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Resets the fd set identified by the
|
||||
* given type
|
||||
*
|
||||
* @param type Type of set to reset
|
||||
*/
|
||||
void reset(fdset_type type);
|
||||
|
||||
/**
|
||||
* Checks if the given fd is set in the fdset
|
||||
* identified by the given type. If fd
|
||||
* is set true returned.
|
||||
*
|
||||
* @param type Requested fd set type
|
||||
* @param fd fd to check
|
||||
* @return True if fd is set in fdset
|
||||
*/
|
||||
bool is_set(fdset_type type, int fd) const;
|
||||
|
||||
/**
|
||||
* Clears the given fd is set in the fdset
|
||||
* identified by the given type.
|
||||
*
|
||||
* @param type Requested fd set type
|
||||
* @param fd fd to clear
|
||||
*/
|
||||
void clear(fdset_type type, int fd);
|
||||
|
||||
/**
|
||||
* Sets the bit for the given fd is set
|
||||
* in the fdset identified by the given type.
|
||||
*
|
||||
* @param type Requested fd set type
|
||||
* @param fd fd to set
|
||||
*/
|
||||
void set(fdset_type type, int fd);
|
||||
|
||||
private:
|
||||
fdset fdsets_[3];
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_SELECT_FDSETS_HPP
|
||||
@@ -0,0 +1,167 @@
|
||||
#ifndef MATADOR_SOCKET_HPP
|
||||
#define MATADOR_SOCKET_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace matador {
|
||||
|
||||
/**
|
||||
* Base class for some kind of socket
|
||||
* classes (acceptor, stream) representing a
|
||||
* socket. The protocol is selected via the
|
||||
* template parameter (/sa tcp and udp classes)
|
||||
*
|
||||
* @tparam P Protocol type
|
||||
*/
|
||||
template < class P >
|
||||
class socket_base {
|
||||
public:
|
||||
typedef P protocol_type; /**< Shortcut to the protocol type */
|
||||
typedef typename P::peer peer_type; /**< Shortcut to the peer type */
|
||||
|
||||
/**
|
||||
* Creates a socket for a specific given
|
||||
* protocol
|
||||
*
|
||||
* @param protocol Initial protocol
|
||||
*/
|
||||
explicit socket_base(const protocol_type &protocol);
|
||||
|
||||
/**
|
||||
* Creates a socket with the given peer
|
||||
*
|
||||
* @param peer Peer used to initialise the socket
|
||||
*/
|
||||
explicit socket_base(const peer_type &peer);
|
||||
|
||||
/**
|
||||
* Opens a socket. On success a positive socket id (fd)
|
||||
* is returned. In case of error -1 is returned and
|
||||
* errno is set.
|
||||
*
|
||||
* @param protocol Protocol for which a socket is created
|
||||
* @return The socket fd or -1 on error
|
||||
*/
|
||||
socket_type open(const protocol_type &protocol);
|
||||
|
||||
/**
|
||||
* Closes the open socket
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Returns true if the socket is open (created)
|
||||
*
|
||||
* @return True on open socket
|
||||
*/
|
||||
bool is_open() const;
|
||||
|
||||
/**
|
||||
* Releases the socket fd and sets
|
||||
* the internal socket to zero (0).
|
||||
*
|
||||
* After the socket is released, the user
|
||||
* is in charge to take of the socket
|
||||
*
|
||||
* @return The released socket fd
|
||||
*/
|
||||
socket_type release();
|
||||
|
||||
/**
|
||||
* Connect to the given peer. If the connection
|
||||
* could be established, true is returned,
|
||||
* otherwise false is returned, and errno is set.
|
||||
*
|
||||
* @param p Peer to execute to
|
||||
* @return True on successful connection
|
||||
*/
|
||||
bool connect(const typename protocol_type::peer &p);
|
||||
|
||||
/**
|
||||
* Sets the socket either blocking (false) or
|
||||
* non-blocking (true).
|
||||
*
|
||||
* @param nb True sets the socket non blocking false blocking
|
||||
*/
|
||||
void non_blocking(bool nb);
|
||||
|
||||
/**
|
||||
* Returns true if the socket is non-blocking
|
||||
* otherwise returns false
|
||||
* @return True if the socket is non-blocking
|
||||
*/
|
||||
bool non_blocking() const;
|
||||
|
||||
/**
|
||||
* Set or unset the cose on the exec flag
|
||||
* for the socket
|
||||
*
|
||||
* @param nb Flag to set or unset cloexec option
|
||||
*/
|
||||
void cloexec(bool nb);
|
||||
|
||||
/**
|
||||
* Returns true if the close-on-exec option is set
|
||||
*
|
||||
* @return True on set cloexec option
|
||||
*/
|
||||
bool cloexec() const;
|
||||
|
||||
/**
|
||||
* Sets a socket option represented by name. If the option
|
||||
* was successfully set, true is returned. Otherwise, false
|
||||
* and errno ist set.
|
||||
*
|
||||
* @param name Option name
|
||||
* @param value Flag to set or unset the option
|
||||
* @return True on success
|
||||
*/
|
||||
bool options(int name, bool value);
|
||||
|
||||
/**
|
||||
* Returns the underlying socket fd
|
||||
*
|
||||
* @return Underlying socket fd
|
||||
*/
|
||||
socket_type id() const;
|
||||
|
||||
/**
|
||||
* Assigns the given socket fd to this
|
||||
* socket. If the socket is already opened,
|
||||
* an exception is thrown.
|
||||
*
|
||||
* @param sock The socket fd to assign
|
||||
*/
|
||||
void assign(socket_type sock);
|
||||
|
||||
protected:
|
||||
/// @cond MATADOR_DEV
|
||||
socket_base() = default;
|
||||
~socket_base() = default;
|
||||
|
||||
socket_type open(int family, int type, int protocol);
|
||||
|
||||
socket_type sock_ = 0;
|
||||
std::string name_;
|
||||
#ifdef _WIN32
|
||||
bool is_nonblocking_ = false;
|
||||
#endif
|
||||
/// @endcond
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "matador/net/socket.tpp"
|
||||
|
||||
#endif //MATADOR_SOCKET_HPP
|
||||
@@ -0,0 +1,257 @@
|
||||
#include "matador/net/socket.hpp"
|
||||
#include "matador/net/socket_stream.hpp"
|
||||
#include "matador/net/os.hpp"
|
||||
#include "matador/net/error.hpp"
|
||||
#include "matador/net/ip.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace matador {
|
||||
|
||||
#define throw_logic_error(msg) \
|
||||
do { \
|
||||
std::stringstream str; \
|
||||
str << msg; \
|
||||
throw std::logic_error(str.str()); \
|
||||
} while(false);
|
||||
|
||||
template < class P >
|
||||
socket_base<P>::socket_base(const protocol_type &protocol)
|
||||
{
|
||||
open(protocol);
|
||||
}
|
||||
|
||||
template < class P >
|
||||
socket_base<P>::socket_base(const peer_type &peer)
|
||||
{
|
||||
open(peer.protocol());
|
||||
}
|
||||
|
||||
template < class P >
|
||||
socket_type socket_base<P>::open(const protocol_type &protocol)
|
||||
{
|
||||
return open(protocol.family(), protocol.type(), protocol.protocol());
|
||||
}
|
||||
|
||||
template < class P >
|
||||
void socket_base<P>::close()
|
||||
{
|
||||
if (sock_ <= 0) {
|
||||
return;
|
||||
}
|
||||
//os::shutdown(sock_, os::shutdown_type::READ_WRITE);
|
||||
os::close(sock_);
|
||||
sock_ = 0;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
bool socket_base<P>::is_open() const
|
||||
{
|
||||
return sock_ > 0;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
socket_type socket_base<P>::release()
|
||||
{
|
||||
auto tmp_fd = sock_;
|
||||
sock_ = 0;
|
||||
return tmp_fd;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
bool socket_base<P>::connect(const typename protocol_type::peer &p)
|
||||
{
|
||||
return ::connect(sock_, p.data(), static_cast<int>(p.size())) == 0;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
void socket_base<P>::non_blocking(bool nb)
|
||||
{
|
||||
#ifdef WIN32
|
||||
unsigned long nonblock = nb ? 0 : 1;
|
||||
// fcntl doesn't do the right thing, but the similar ioctl does
|
||||
// warning: is that still true? and does it the right thing for
|
||||
// set blocking as well?
|
||||
if (ioctlsocket(sock_, FIONBIO, &nonblock) != 0) {
|
||||
throw std::logic_error("ioctlsocket: couldn't set flags");
|
||||
}
|
||||
is_nonblocking_ = nb;
|
||||
#else
|
||||
int val = fcntl(sock_, F_GETFL, 0);
|
||||
if (val < 0) {
|
||||
throw std::logic_error("fcntl: couldn't get flags");
|
||||
}
|
||||
|
||||
int flag = (nb ? O_NONBLOCK : 0);
|
||||
if (fcntl(sock_, F_SETFL, val | flag) < 0) {
|
||||
std::string err(strerror(errno));
|
||||
throw std::logic_error("fcntl: couldn't set flags (" + err + ")");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template < class P >
|
||||
bool socket_base<P>::non_blocking() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return is_nonblocking_;
|
||||
#else
|
||||
int val = fcntl(sock_, F_GETFL, 0);
|
||||
if (val < 0) {
|
||||
std::string err(strerror(errno));
|
||||
throw std::logic_error("fcntl: couldn't get flags (" + err + ")");
|
||||
}
|
||||
return (val & O_NONBLOCK) > 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
template < class P >
|
||||
void socket_base<P>::cloexec(bool nb)
|
||||
{
|
||||
#ifdef WIN32
|
||||
unsigned long cloexec = 1;
|
||||
// fcntl doesn't do the right thing, but the simular ioctl does
|
||||
// warning: is that still true? and does it the right thing for
|
||||
// set blocking as well?
|
||||
if (ioctlsocket(sock_, FIONBIO, &cloexec) != 0) {
|
||||
throw std::logic_error("ioctlsocket: couldn't set flags");
|
||||
}
|
||||
#else
|
||||
int val = fcntl(sock_, F_GETFL, 0);
|
||||
if (val < 0) {
|
||||
throw std::logic_error("fcntl: couldn't get flags");
|
||||
}
|
||||
|
||||
int flag = (nb ? FD_CLOEXEC : 0);
|
||||
if (fcntl(sock_, F_SETFL, val | flag) < 0) {
|
||||
std::string err(strerror(errno));
|
||||
throw std::logic_error("fcntl: couldn't set flags (" + err + ")");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template < class P >
|
||||
bool socket_base<P>::cloexec() const
|
||||
{
|
||||
int val = fcntl(sock_, F_GETFL, 0);
|
||||
if (val < 0) {
|
||||
std::string err(strerror(errno));
|
||||
throw std::logic_error("fcntl: couldn't get flags (" + err + ")");
|
||||
}
|
||||
return (val & FD_CLOEXEC) > 0;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
bool socket_base<P>::options(int name, bool value)
|
||||
{
|
||||
const char flag = static_cast<char>(value ? 1 : 0);
|
||||
return setsockopt(sock_, IPPROTO_TCP, name, &flag, sizeof(flag)) == 0;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
socket_type socket_base<P>::id() const
|
||||
{
|
||||
return sock_;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
void socket_base<P>::assign(socket_type sock)
|
||||
{
|
||||
if (is_open()) {
|
||||
throw std::logic_error("couldn't assign: socket already opened");
|
||||
}
|
||||
|
||||
struct sockaddr_in addr{};
|
||||
socklen_t addr_size = sizeof(struct sockaddr_in);
|
||||
if (getpeername(sock, (struct sockaddr *)&addr, &addr_size) == 0) {
|
||||
//char *clientip = new char[20];
|
||||
char s[INET6_ADDRSTRLEN];
|
||||
os::inet_ntop(addr.sin_family, &addr.sin_addr, s, sizeof s);
|
||||
#ifdef _WIN32
|
||||
// strcpy_s(clientip, 20, s);
|
||||
#else
|
||||
// strcpy(clientip, s);
|
||||
#endif
|
||||
}
|
||||
sock_ = sock;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
socket_type socket_base<P>::open(int family, int type, int protocol)
|
||||
{
|
||||
sock_ = ::socket(family, type, protocol);
|
||||
return sock_;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
socket_type connect(socket_stream<P> &stream, const char* hostname, unsigned short port)
|
||||
{
|
||||
char portstr[6];
|
||||
sprintf(portstr, "%d", port);
|
||||
// const char* portname = "daytime";
|
||||
struct addrinfo hints = {};
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
struct addrinfo* res = nullptr;
|
||||
struct addrinfo* head = nullptr;
|
||||
int err = getaddrinfo(hostname, portstr, &hints, &res);
|
||||
if (err != 0) {
|
||||
detail::throw_logic_error_with_gai_errno("failed to resolve local socket address: %s", err);
|
||||
}
|
||||
|
||||
head = res;
|
||||
|
||||
socket_type conn_fd = 0;
|
||||
socket_type ret = 0;
|
||||
do {
|
||||
conn_fd = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (!is_valid_socket(conn_fd)) {
|
||||
// error, try the next one
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = ::connect(conn_fd, res->ai_addr, res->ai_addrlen);
|
||||
if (ret == 0) {
|
||||
// success
|
||||
stream.assign(conn_fd);
|
||||
break;
|
||||
// } else {
|
||||
// throw_logic_error("couldn't execute: " << strerror(errno));
|
||||
}
|
||||
|
||||
// bind error, close and try the next one
|
||||
os::shutdown(conn_fd, os::shutdown_type::READ_WRITE);
|
||||
} while ( (res = res->ai_next) != nullptr);
|
||||
|
||||
if (res == nullptr) {
|
||||
throw_logic_error("couldn't execute to " << hostname << ":" << port);
|
||||
}
|
||||
|
||||
freeaddrinfo(head);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
int connect(socket_stream<P> &stream, peer_base<P> endpoint)
|
||||
{
|
||||
auto pt = endpoint.protocol();
|
||||
|
||||
auto fd = ::socket(pt.family(), pt.type(), pt.protocol());
|
||||
|
||||
if (!is_valid_socket(fd)) {
|
||||
return static_cast<int>(fd);
|
||||
}
|
||||
|
||||
auto ret = ::connect(fd, endpoint.data(), static_cast<int>(endpoint.size()));
|
||||
if (ret == 0) {
|
||||
stream.assign(fd);
|
||||
} else {
|
||||
os::shutdown(fd, os::shutdown_type::READ_WRITE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
#ifndef MATADOR_SOCKET_ACCEPTOR_HPP
|
||||
#define MATADOR_SOCKET_ACCEPTOR_HPP
|
||||
|
||||
#include "matador/net/socket.hpp"
|
||||
|
||||
namespace matador {
|
||||
|
||||
/**
|
||||
* The socket acceptor class provides the
|
||||
* base functionality of the socket base class
|
||||
* plus acceptor specific functions like
|
||||
* listen, bind or accept
|
||||
*
|
||||
* @tparam P Socket protocol type
|
||||
*/
|
||||
template < class P >
|
||||
class socket_acceptor : public socket_base<P>
|
||||
{
|
||||
public:
|
||||
typedef socket_base<P> base; /**< Shortcut to base socket type */
|
||||
typedef socket_stream<P> stream_type; /**< Shortcut to socket stream type */
|
||||
typedef typename base::protocol_type protocol_type; /**< Shortcut to protocol type */
|
||||
typedef typename base::peer_type peer_type; /**< Shortcut to peer type */
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
socket_acceptor() = default;
|
||||
|
||||
/**
|
||||
* Constructs a socket acceptor for
|
||||
* the given peer
|
||||
*
|
||||
* @param peer Peer to construct an acceptor from
|
||||
*/
|
||||
explicit socket_acceptor(peer_type &peer);
|
||||
|
||||
/**
|
||||
* Constructs an acceptor for given hostname and port
|
||||
*
|
||||
* @param hostname Hostname of the accepting endpoint
|
||||
* @param port Portnumber of the accepting endpoint
|
||||
*/
|
||||
socket_acceptor(const char* hostname, unsigned short port);
|
||||
|
||||
/**
|
||||
* Creates a listening socket and binds
|
||||
* the given hostname and port to it
|
||||
*
|
||||
* Returns zero (0) on success and -1 on error
|
||||
* with errno set
|
||||
*
|
||||
* @param hostname Hostname to bind
|
||||
* @param port Portnumber to bind
|
||||
* @return Returns zero (0) on success.
|
||||
*/
|
||||
int bind(const char* hostname, unsigned short port);
|
||||
|
||||
/**
|
||||
* Creates a listening socket and binds
|
||||
* the given peer endpoint to it
|
||||
*
|
||||
* Returns zero (0) on success and -1 on error
|
||||
* with errno set
|
||||
*
|
||||
* @param peer Binds the given peer endpoint to the socket
|
||||
* @return Returns zero (0) on success.
|
||||
*/
|
||||
int bind(peer_type &peer);
|
||||
|
||||
/**
|
||||
* Start listening to the bound endpoint using
|
||||
* the internally created socket.
|
||||
*
|
||||
* Returns zero (0) on success and -1 on error
|
||||
* with errno set
|
||||
*
|
||||
* @param backlog Number of backlog
|
||||
* @return Returns zero (0) on success.
|
||||
*/
|
||||
int listen(int backlog);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the underlying
|
||||
* concrete internet address of the given
|
||||
* socket address structure
|
||||
*
|
||||
* @param sa Socket address
|
||||
* @return Pointer to the internet address
|
||||
*/
|
||||
void* get_in_addr(struct sockaddr *sa);
|
||||
|
||||
/**
|
||||
* Returns the port number of the given
|
||||
* socket address structure
|
||||
*
|
||||
* @param sa Socket address
|
||||
* @return The port number
|
||||
*/
|
||||
unsigned short get_port(struct sockaddr *sa);
|
||||
|
||||
/**
|
||||
* Get the remote address and port as string
|
||||
* representation.
|
||||
*
|
||||
* @param remote_addr Remote socket address structure
|
||||
* @return String representation of the remote address
|
||||
*/
|
||||
std::string get_remote_address(struct sockaddr_storage &remote_addr);
|
||||
|
||||
/**
|
||||
* Accept a connection and assign the socket descriptor
|
||||
* to the given socket stream.
|
||||
*
|
||||
* Once the descriptor is assigned to the stream it
|
||||
* can be used to read and write data to it.
|
||||
*
|
||||
* @param stream Stream to use after connection was accepted
|
||||
* @return The fd of the new connection
|
||||
*/
|
||||
int accept(stream_type &stream);
|
||||
|
||||
/**
|
||||
* Accept a connection and assign the socket descriptor
|
||||
* to the given socket stream.
|
||||
*
|
||||
* Once the descriptor is assigned to the stream it
|
||||
* can be used to read and write data to it.
|
||||
*
|
||||
* The given peer endpoint is filled with the
|
||||
* address information of the remote endpoint.
|
||||
*
|
||||
* @param stream Stream to use after connection was accepted
|
||||
* @param endpoint Will be filled with the remote endpoint information
|
||||
* @return The fd of the new connection
|
||||
*/
|
||||
int accept(stream_type &stream, peer_type &endpoint);
|
||||
|
||||
/**
|
||||
* Sets or clears the reuse address flag. If the
|
||||
* given value is true the flag is set otherwise the
|
||||
* flag is cleared.
|
||||
*
|
||||
* @param reuse Indicates if the reuse address flag should be set
|
||||
* @return 0 if setting was successful, -1 on error
|
||||
*/
|
||||
int reuse_address(bool reuse);
|
||||
|
||||
/**
|
||||
* Returns true if the flag is set otherwise
|
||||
* false is returned.
|
||||
*
|
||||
* @return True if flag is set
|
||||
*/
|
||||
bool reuse_address() const;
|
||||
};
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
template < class P >
|
||||
socket_acceptor<P>::socket_acceptor(peer_type &peer)
|
||||
: socket_base<P>(peer)
|
||||
{
|
||||
bind(peer);
|
||||
}
|
||||
|
||||
template < class P >
|
||||
socket_acceptor<P>::socket_acceptor(const char* hostname, unsigned short port)
|
||||
{
|
||||
bind(hostname, port);
|
||||
}
|
||||
|
||||
template < class P >
|
||||
int socket_acceptor<P>::bind(const char* hostname, unsigned short port)
|
||||
{
|
||||
char portstr[6];
|
||||
sprintf(portstr, "%d", port);
|
||||
// const char* portname = "daytime";
|
||||
struct addrinfo hints = {};
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
|
||||
struct addrinfo* res = nullptr;
|
||||
struct addrinfo* head = nullptr;
|
||||
int err = getaddrinfo(hostname, portstr, &hints, &res);
|
||||
if (err != 0) {
|
||||
throw_logic_error("failed to resolve local socket address (error: " << err << ")");
|
||||
}
|
||||
|
||||
head = res;
|
||||
|
||||
int listenfd = 0;
|
||||
int ret = 0;
|
||||
const int on = 1;
|
||||
do {
|
||||
listenfd = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (listenfd < 0) {
|
||||
// error, try next one
|
||||
continue;
|
||||
}
|
||||
|
||||
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
throw std::logic_error(strerror(errno));
|
||||
}
|
||||
|
||||
ret = ::bind(listenfd, res->ai_addr, res->ai_addrlen);
|
||||
if (ret == 0) {
|
||||
// success
|
||||
this->assign(listenfd);
|
||||
break;
|
||||
} else {
|
||||
throw_logic_error("couldn't bind to " << hostname << ":" << port << ": " << strerror(errno));
|
||||
}
|
||||
|
||||
// bind error, close and try next one
|
||||
this->close();
|
||||
} while ( (res = res->ai_next) != nullptr);
|
||||
|
||||
if (res == nullptr) {
|
||||
throw_logic_error("couldn't bind to " << hostname << ":" << port);
|
||||
}
|
||||
|
||||
freeaddrinfo(head);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
int socket_acceptor<P>::bind(peer_type &peer)
|
||||
{
|
||||
socket_type listen_fd = ::socket(peer.protocol().family(), peer.protocol().type(), peer.protocol().protocol());
|
||||
if (!is_valid_socket(listen_fd)) {
|
||||
// error, try next one
|
||||
return static_cast<int>(listen_fd);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const char on = 1;
|
||||
#else
|
||||
const int on = 1;
|
||||
#endif
|
||||
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
detail::throw_logic_error_with_errno("setsockopt error: %s", errno);
|
||||
}
|
||||
|
||||
int ret = ::bind(listen_fd, peer.data(), static_cast<int>(peer.size()));
|
||||
if (ret == 0) {
|
||||
// success
|
||||
this->assign(listen_fd);
|
||||
} else {
|
||||
detail::throw_logic_error_with_errno("couldn't bind fd: %s", errno);
|
||||
}
|
||||
size_t s = peer.size();
|
||||
ret = getsockname(this->id(), peer.data(), (socklen_t*)&s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template < class P >
|
||||
int socket_acceptor<P>::listen(int backlog)
|
||||
{
|
||||
return ::listen(this->id(), backlog);
|
||||
}
|
||||
|
||||
template < class P >
|
||||
void* socket_acceptor<P>::get_in_addr(struct sockaddr *sa)
|
||||
{
|
||||
if (sa->sa_family == AF_INET) {
|
||||
return &(((struct sockaddr_in*)sa)->sin_addr);
|
||||
} else {
|
||||
return &(((struct sockaddr_in6*)sa)->sin6_addr);
|
||||
}
|
||||
}
|
||||
|
||||
template < class P >
|
||||
unsigned short socket_acceptor<P>::get_port(struct sockaddr *sa)
|
||||
{
|
||||
if (sa->sa_family == AF_INET) {
|
||||
return ntohs(((struct sockaddr_in*)sa)->sin_port);
|
||||
} else {
|
||||
return ntohs(((struct sockaddr_in6*)sa)->sin6_port);
|
||||
}
|
||||
}
|
||||
|
||||
template < class P >
|
||||
std::string socket_acceptor<P>::get_remote_address(struct sockaddr_storage &remote_addr)
|
||||
{
|
||||
char s[INET6_ADDRSTRLEN];
|
||||
|
||||
os::inet_ntop(remote_addr.ss_family,
|
||||
get_in_addr((struct sockaddr *)&remote_addr),
|
||||
s, sizeof s);
|
||||
|
||||
std::stringstream ra;
|
||||
|
||||
ra << s << ":" << get_port((struct sockaddr *)&remote_addr);
|
||||
return ra.str();
|
||||
}
|
||||
|
||||
template < class P >
|
||||
int socket_acceptor<P>::accept(stream_type &stream)
|
||||
{
|
||||
struct sockaddr_storage remote_addr = {};
|
||||
// address_type remote_addr;
|
||||
socklen_t addrlen = sizeof(remote_addr);
|
||||
auto fd = ::accept(this->id(), (struct sockaddr *)&remote_addr, &addrlen);
|
||||
|
||||
if (is_valid_socket(fd)) {
|
||||
stream.assign(fd);
|
||||
stream.non_blocking(true);
|
||||
stream.cloexec(true);
|
||||
// } else {
|
||||
// detail::throw_logic_error_with_errno("accept failed: %s", errno);
|
||||
}
|
||||
|
||||
return static_cast<int>(fd);
|
||||
}
|
||||
|
||||
template<class P>
|
||||
int socket_acceptor<P>::accept(stream_type &stream, peer_type &endpoint)
|
||||
{
|
||||
auto addr_len = static_cast<socklen_t>(endpoint.size());
|
||||
auto fd = ::accept(this->id(), endpoint.data(), &addr_len);
|
||||
|
||||
if (is_valid_socket(fd)) {
|
||||
stream.assign(fd);
|
||||
stream.non_blocking(true);
|
||||
stream.cloexec(true);
|
||||
// } else {
|
||||
// detail::throw_logic_error_with_errno("accept failed: %s", errno);
|
||||
}
|
||||
|
||||
return static_cast<int>(fd);
|
||||
}
|
||||
|
||||
template < class P >
|
||||
int socket_acceptor<P>::reuse_address(bool reuse)
|
||||
{
|
||||
const int option(reuse ? 1 : 0);
|
||||
return setsockopt(this->id(), SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(option));
|
||||
}
|
||||
|
||||
template < class P >
|
||||
bool socket_acceptor<P>::reuse_address() const
|
||||
{
|
||||
size_t option {};
|
||||
socklen_t i;
|
||||
i = sizeof(option);
|
||||
getsockopt(this->id(), SOL_SOCKET, SO_REUSEADDR, (char*)&option, &i);
|
||||
return option > 0;
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_SOCKET_ACCEPTOR_HPP
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef MATADOR_SOCKET_INTERRUPTER_HPP
|
||||
#define MATADOR_SOCKET_INTERRUPTER_HPP
|
||||
|
||||
#include "matador/net/ip.hpp"
|
||||
|
||||
#include "matador/logger/logger.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace matador::net {
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
|
||||
class OOS_NET_API socket_interrupter
|
||||
{
|
||||
public:
|
||||
socket_interrupter();
|
||||
~socket_interrupter();
|
||||
|
||||
socket_type socket_id() const;
|
||||
|
||||
void interrupt();
|
||||
bool reset();
|
||||
|
||||
private:
|
||||
tcp::socket server_;
|
||||
tcp::socket client_;
|
||||
|
||||
logger::logger log_;
|
||||
|
||||
std::array<char, 1> indicator_ = { { 0 } };
|
||||
std::array<char, 1> consumer_ = {};
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
}
|
||||
|
||||
#endif //MATADOR_SOCKET_INTERRUPTER_HPP
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef MATADOR_SOCKET_STREAM_HPP
|
||||
#define MATADOR_SOCKET_STREAM_HPP
|
||||
|
||||
#ifdef _WIN64
|
||||
#define ssize_t __int64
|
||||
#elif _WIN32
|
||||
#define ssize_t long
|
||||
#endif
|
||||
|
||||
#include "matador/net/socket.hpp"
|
||||
|
||||
namespace matador {
|
||||
|
||||
/**
|
||||
* The class represents a read write socket. It is
|
||||
* independent to the protocol type (UDP or TCP).
|
||||
*
|
||||
* It provides to methods receive and send to read
|
||||
* or write data to an open socket.
|
||||
*
|
||||
* @tparam P Protocol type of the socket
|
||||
*/
|
||||
template < class P >
|
||||
class socket_stream : public socket_base<P>
|
||||
{
|
||||
public:
|
||||
typedef socket_base<P> base; /**< Shortcut to socket base class */
|
||||
typedef typename base::protocol_type protocol_type; /**< Shortcut to protocol type */
|
||||
typedef typename base::peer_type peer_type; /**< Shortcut to the peer type */
|
||||
|
||||
/**
|
||||
* Creates an uninitialized socket stream
|
||||
*/
|
||||
socket_stream() = default;
|
||||
|
||||
/**
|
||||
* Creates a socket stream for the given
|
||||
* protocol type.
|
||||
*
|
||||
* @param protocol Type of the protocol
|
||||
*/
|
||||
explicit socket_stream(const protocol_type &protocol);
|
||||
|
||||
/**
|
||||
* Receives data from the underlying open socket.
|
||||
* All received data is stored in the given buffer.
|
||||
* The limit of the data to receive is the size of
|
||||
* the buffer
|
||||
*
|
||||
* Number of bytes is returned. If returned value is zero (0)
|
||||
* the socket was closed. If the return value is -1 an error
|
||||
* occurred or the socket was blocked (in case of nonblocking)
|
||||
* The concrete error code and message can be retrieved with
|
||||
* errno.
|
||||
*
|
||||
* @tparam Buffer type of the buffer object.
|
||||
* @param buffer The buffer object.
|
||||
* @return The number of bytes received, -1 on error or 0 on close
|
||||
*/
|
||||
template < class Buffer >
|
||||
ssize_t receive(Buffer &buffer);
|
||||
|
||||
/**
|
||||
*
|
||||
* @tparam Buffer type of the buffer object.
|
||||
* @param buffer The buffer object.
|
||||
* @return The number of bytes sent, -1 on error or 0 on close
|
||||
*/
|
||||
template < class Buffer >
|
||||
ssize_t send(const Buffer &buffer);
|
||||
};
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
template < class P >
|
||||
socket_stream<P>::socket_stream(const protocol_type &protocol)
|
||||
: base(protocol)
|
||||
{}
|
||||
|
||||
template < class P >
|
||||
template < class Buffer >
|
||||
ssize_t socket_stream<P>::receive(Buffer &buffer)
|
||||
{
|
||||
return ::recv(this->id(), buffer.data(), static_cast<int>(buffer.capacity()), 0);
|
||||
}
|
||||
|
||||
template < class P >
|
||||
template < class Buffer >
|
||||
ssize_t socket_stream<P>::send(const Buffer &buffer)
|
||||
{
|
||||
return ::send(this->id(), buffer.data(), static_cast<int>(buffer.size()), 0);
|
||||
}
|
||||
/// @endcond
|
||||
}
|
||||
|
||||
#endif //MATADOR_SOCKET_STREAM_HPP
|
||||
@@ -0,0 +1,96 @@
|
||||
#ifndef MATADOR_STREAM_HANDLER_HPP
|
||||
#define MATADOR_STREAM_HANDLER_HPP
|
||||
|
||||
#include "matador/utils/buffer_view.hpp"
|
||||
|
||||
#include "matador/logger/logger.hpp"
|
||||
|
||||
#include "matador/net/export.hpp"
|
||||
#include "matador/net/handler.hpp"
|
||||
#include "matador/net/ip.hpp"
|
||||
#include "matador/net/io_stream.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
namespace matador {
|
||||
|
||||
class handler_creator;
|
||||
|
||||
/**
|
||||
* The stream_handler class implements the
|
||||
* handler and io_stream interface and is
|
||||
* used with the io_service to handle socket
|
||||
* connections more easily in comparison
|
||||
* with the plain reactor boilerplate.
|
||||
*
|
||||
* Instances of the class are used internally
|
||||
* within the io_service hiding the read and write
|
||||
* wiring. The user just use the interface
|
||||
* provided by the io_service to setup
|
||||
* a server.
|
||||
*/
|
||||
class OOS_NET_API stream_handler : public handler, public io_stream
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(tcp::peer, io_stream &)> t_init_handler; /**< Shortcut to the initialize function */
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new stream_handler for the given socket.
|
||||
* The acceptor is the link to the creation source where
|
||||
* the given init function is called when the handler
|
||||
* is initialized
|
||||
*
|
||||
* @param sock Socket to read and write on
|
||||
* @param endpoint Endpoint of the connection
|
||||
* @param creator Pointer to the creating handler class
|
||||
* @param init_handler Initialize function
|
||||
*/
|
||||
stream_handler(tcp::socket sock, tcp::peer endpoint, handler_creator *creator, t_init_handler init_handler);
|
||||
|
||||
void open() override;
|
||||
socket_type handle() const override;
|
||||
void on_input() override;
|
||||
void on_output() override;
|
||||
void on_except() override {}
|
||||
void on_timeout() override {}
|
||||
void on_close() override;
|
||||
void close() override;
|
||||
bool is_ready_write() const override;
|
||||
bool is_ready_read() const override;
|
||||
|
||||
void read(buffer_view buf, t_read_handler read_handler) override;
|
||||
void write(std::list<buffer_view> buffers, t_write_handler write_handler) override;
|
||||
|
||||
void close_stream() override;
|
||||
|
||||
tcp::socket &stream() override;
|
||||
|
||||
std::string name() const override;
|
||||
|
||||
private:
|
||||
logger log_;
|
||||
tcp::socket stream_;
|
||||
tcp::peer endpoint_;
|
||||
|
||||
std::string name_;
|
||||
|
||||
buffer_view read_buffer_;
|
||||
|
||||
std::list<buffer_view> write_buffers_;
|
||||
|
||||
handler_creator *creator_ = nullptr;
|
||||
|
||||
t_init_handler init_handler_;
|
||||
t_read_handler on_read_;
|
||||
t_write_handler on_write_;
|
||||
|
||||
std::atomic_bool is_ready_to_read_ { false };
|
||||
std::atomic_bool is_ready_to_write_ { false };
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_STREAM_HANDLER_HPP
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
|
||||
#define MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
|
||||
|
||||
#include "matador/object/abstract_type_resolver.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
|
||||
namespace matador::object {
|
||||
class abstract_joined_resolver : public abstract_type_resolver {
|
||||
public:
|
||||
[[nodiscard]] const std::type_index& root_type() const;
|
||||
[[nodiscard]] const std::string& collection_name() const;
|
||||
|
||||
protected:
|
||||
explicit abstract_joined_resolver(const std::type_index& root_type, const std::type_index& type, std::string collection_name);
|
||||
|
||||
public:
|
||||
const std::type_index root_type_;
|
||||
const std::string collection_name_;
|
||||
};
|
||||
}
|
||||
#endif // MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef MATADOR_ABSTRACT_TYPE_RESOLVER_HPP
|
||||
#define MATADOR_ABSTRACT_TYPE_RESOLVER_HPP
|
||||
|
||||
#include <typeindex>
|
||||
|
||||
namespace matador::object {
|
||||
class abstract_type_resolver {
|
||||
public:
|
||||
virtual ~abstract_type_resolver() = default;
|
||||
|
||||
[[nodiscard]] const std::type_index& type() const { return type_; }
|
||||
|
||||
protected:
|
||||
explicit abstract_type_resolver(const std::type_index& ti) : type_(ti) {}
|
||||
|
||||
public:
|
||||
const std::type_index type_;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_ABSTRACT_TYPE_RESOLVER_HPP
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef MATADOR_ABSTRACT_TYPE_RESOLVER_FACTORY_HPP
|
||||
#define MATADOR_ABSTRACT_TYPE_RESOLVER_FACTORY_HPP
|
||||
|
||||
#include "matador/object/abstract_type_resolver.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::object {
|
||||
class abstract_type_resolver_factory {
|
||||
public:
|
||||
virtual ~abstract_type_resolver_factory() = default;
|
||||
|
||||
[[nodiscard]] virtual std::shared_ptr<abstract_type_resolver> acquire_object_resolver(const std::type_index &type) const = 0;
|
||||
virtual void register_object_resolver(std::shared_ptr<abstract_type_resolver> &&resolver) = 0;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_ABSTRACT_TYPE_RESOLVER_FACTORY_HPP
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef QUERY_COLUMN_DEFINITION_HPP
|
||||
#define QUERY_COLUMN_DEFINITION_HPP
|
||||
|
||||
#include "matador/utils/basic_types.hpp"
|
||||
#include "matador/utils/default_type_traits.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
enum class null_option_type : uint8_t {
|
||||
Nullable, NotNull
|
||||
};
|
||||
|
||||
class object;
|
||||
|
||||
class attribute {
|
||||
public:
|
||||
explicit attribute(std::string name); // NOLINT(*-explicit-constructor)
|
||||
|
||||
attribute(const attribute&) = default;
|
||||
attribute& operator=(const attribute&) = default;
|
||||
attribute(attribute&&) noexcept = default;
|
||||
attribute& operator=(attribute&&) noexcept = default;
|
||||
|
||||
attribute() = default;
|
||||
attribute(std::string name,
|
||||
utils::basic_type type,
|
||||
const utils::field_attributes &attr = NullAttributes,
|
||||
null_option_type null_opt = null_option_type::NotNull);
|
||||
|
||||
[[nodiscard]] const std::string& name() const;
|
||||
void name(const std::string& n);
|
||||
[[nodiscard]] std::string full_name() const;
|
||||
[[nodiscard]] size_t index() const;
|
||||
[[nodiscard]] const utils::field_attributes& attributes() const;
|
||||
[[nodiscard]] utils::field_attributes& attributes();
|
||||
[[nodiscard]] bool is_nullable() const;
|
||||
[[nodiscard]] utils::basic_type type() const;
|
||||
[[nodiscard]] std::shared_ptr<object> owner() const;
|
||||
void change_type(utils::basic_type type, const utils::field_attributes &attr = NullAttributes);
|
||||
template < typename Type >
|
||||
void change_type(const utils::field_attributes &attr = NullAttributes) {
|
||||
type_ = utils::data_type_traits<Type>::type(attr.size());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_integer() const;
|
||||
[[nodiscard]] bool is_floating_point() const;
|
||||
[[nodiscard]] bool is_bool() const;
|
||||
[[nodiscard]] bool is_string() const;
|
||||
[[nodiscard]] bool is_varchar() const;
|
||||
[[nodiscard]] bool is_date() const;
|
||||
[[nodiscard]] bool is_time() const;
|
||||
[[nodiscard]] bool is_timestamp() const;
|
||||
[[nodiscard]] bool is_blob() const;
|
||||
[[nodiscard]] bool is_null() const;
|
||||
|
||||
template< typename Type >
|
||||
[[nodiscard]] bool is_type_of() const {
|
||||
return type() == utils::data_type_traits<Type>::type(attributes().size());
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const attribute& attr);
|
||||
|
||||
private:
|
||||
friend class object;
|
||||
friend class object_generator;
|
||||
|
||||
std::string name_;
|
||||
size_t index_{0};
|
||||
std::weak_ptr<object> owner_;
|
||||
utils::basic_type type_{utils::basic_type::Null};
|
||||
utils::field_attributes options_{};
|
||||
null_option_type null_option_{null_option_type::NotNull};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_COLUMN_DEFINITION_HPP
|
||||
@@ -1,206 +0,0 @@
|
||||
#ifndef QUERY_COLUMN_DEFINITION_HPP
|
||||
#define QUERY_COLUMN_DEFINITION_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "matador/utils/basic_types.hpp"
|
||||
#include "matador/utils/default_type_traits.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
#include "matador/utils/value.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
enum class null_option : uint8_t {
|
||||
NULLABLE, NOT_NULL
|
||||
};
|
||||
|
||||
class attribute_definition {
|
||||
public:
|
||||
explicit attribute_definition(const char *name); // NOLINT(*-explicit-constructor)
|
||||
explicit attribute_definition(std::string name); // NOLINT(*-explicit-constructor)
|
||||
|
||||
attribute_definition(const attribute_definition&) = default;
|
||||
attribute_definition& operator=(const attribute_definition&) = default;
|
||||
attribute_definition(attribute_definition&&) noexcept = default;
|
||||
attribute_definition& operator=(attribute_definition&&) noexcept = default;
|
||||
|
||||
template<typename Type>
|
||||
explicit attribute_definition(std::string name, std::string table_name, const utils::field_attributes& attr)
|
||||
: attribute_definition(std::move(name), std::move(table_name), utils::data_type_traits<Type>::type(attr.size()), attr)
|
||||
{}
|
||||
|
||||
attribute_definition(std::string name, std::string table_name, utils::basic_type type, const utils::field_attributes& attr);
|
||||
|
||||
template<typename Type>
|
||||
explicit attribute_definition(std::string name, const utils::field_attributes& attr)
|
||||
: attribute_definition(std::move(name), utils::data_type_traits<Type>::type(attr.size()), attr)
|
||||
{}
|
||||
|
||||
template<typename Type>
|
||||
attribute_definition(std::string name, const Type &, const utils::field_attributes& attr, null_option null_opt)
|
||||
: attribute_definition(std::move(name), utils::data_type_traits<Type>::type(attr.size()), attr, null_opt)
|
||||
{}
|
||||
|
||||
template<size_t SIZE>
|
||||
attribute_definition(std::string name, const char (&)[SIZE], const utils::field_attributes& attr, const null_option null_opt)
|
||||
: attribute_definition(std::move(name), utils::data_type_traits<const char*>::type(attr.size()), attr, null_opt)
|
||||
{}
|
||||
|
||||
attribute_definition(std::string name, utils::basic_type type, const utils::field_attributes&, null_option null_opt, size_t index = 0);
|
||||
attribute_definition(std::string name, std::string table_name, utils::basic_type type, const utils::field_attributes&, null_option null_opt, size_t index = 0);
|
||||
|
||||
template<typename Type>
|
||||
attribute_definition(std::string name, const std::shared_ptr<attribute_definition> &ref_column, const utils::field_attributes& attr, null_option null_opt)
|
||||
: attribute_definition(std::move(name), utils::data_type_traits<Type>::type(attr.size()), ref_column, attr, null_opt)
|
||||
{}
|
||||
|
||||
attribute_definition(std::string name, utils::basic_type type, size_t index, const std::shared_ptr<attribute_definition> &ref_column, const utils::field_attributes& attr, null_option null_opt);
|
||||
|
||||
[[nodiscard]] const std::string& name() const;
|
||||
void name(const std::string& n);
|
||||
[[nodiscard]] std::string full_name() const;
|
||||
[[nodiscard]] std::string table_name() const;
|
||||
[[nodiscard]] int index() const;
|
||||
[[nodiscard]] const utils::field_attributes& attributes() const;
|
||||
[[nodiscard]] bool is_nullable() const;
|
||||
[[nodiscard]] utils::basic_type type() const;
|
||||
[[nodiscard]] std::shared_ptr<attribute_definition> reference_column() const;
|
||||
[[nodiscard]] bool is_foreign_reference() const;
|
||||
|
||||
[[nodiscard]] bool is_integer() const;
|
||||
[[nodiscard]] bool is_floating_point() const;
|
||||
[[nodiscard]] bool is_bool() const;
|
||||
[[nodiscard]] bool is_string() const;
|
||||
[[nodiscard]] bool is_varchar() const;
|
||||
[[nodiscard]] bool is_date() const;
|
||||
[[nodiscard]] bool is_time() const;
|
||||
[[nodiscard]] bool is_blob() const;
|
||||
[[nodiscard]] bool is_null() const;
|
||||
|
||||
void type(utils::basic_type type);
|
||||
|
||||
template< typename Type >
|
||||
[[nodiscard]] bool is_type_of() const {
|
||||
return std::holds_alternative<Type>(value_);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string str() const;
|
||||
|
||||
template<typename Type>
|
||||
void set(const Type &value, const utils::field_attributes &attr = utils::null_attributes)
|
||||
{
|
||||
attributes_ = attr;
|
||||
value_ = value;
|
||||
}
|
||||
|
||||
void set(const std::string &value, const utils::field_attributes &attr)
|
||||
{
|
||||
value_ = value;
|
||||
attributes_ = attr;
|
||||
}
|
||||
|
||||
void set(const char *value, const utils::field_attributes &attr)
|
||||
{
|
||||
value_ = std::string(value);
|
||||
attributes_ = attr;
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
std::optional<Type> as() const
|
||||
{
|
||||
return value_.as<Type>();
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream &out, const attribute_definition &col);
|
||||
|
||||
private:
|
||||
template<class Operator>
|
||||
void process(Operator &op)
|
||||
{
|
||||
op.on_attribute(name_.c_str(), value_, attributes_);
|
||||
}
|
||||
|
||||
using data_type_index = std::vector<utils::basic_type>;
|
||||
|
||||
private:
|
||||
static const data_type_index data_type_index_;
|
||||
|
||||
std::string name_;
|
||||
std::string table_;
|
||||
int index_{-1};
|
||||
utils::field_attributes attributes_;
|
||||
null_option null_option_{null_option::NOT_NULL};
|
||||
utils::value value_;
|
||||
std::shared_ptr<attribute_definition> reference_column_;
|
||||
};
|
||||
|
||||
/**
|
||||
* User defined literal to have a shortcut creating a column object
|
||||
* @param name Name of the column
|
||||
* @param type
|
||||
* @param attr Length of the column name
|
||||
* @param null_opt
|
||||
* @return A column object with given name
|
||||
*/
|
||||
attribute_definition make_column(const std::string &name, utils::basic_type type, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL);
|
||||
|
||||
template < typename Type >
|
||||
attribute_definition make_column(const std::string &name, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL)
|
||||
{
|
||||
return make_column(name, utils::data_type_traits<Type>::type(0), attr, null_opt);
|
||||
}
|
||||
template <>
|
||||
attribute_definition make_column<std::string>(const std::string &name, utils::field_attributes attr, null_option null_opt);
|
||||
|
||||
template < typename Type >
|
||||
attribute_definition make_pk_column(const std::string &name, size_t size = 0)
|
||||
{
|
||||
return make_column<Type>(name, { size, utils::constraints::PRIMARY_KEY });
|
||||
}
|
||||
|
||||
template <>
|
||||
attribute_definition make_pk_column<std::string>(const std::string &name, size_t size);
|
||||
|
||||
template < typename Type >
|
||||
attribute_definition make_fk_column(const std::string &name, size_t size, const std::shared_ptr<attribute_definition> &ref_column) {
|
||||
return {name, utils::data_type_traits<Type>::type(size), ref_column, { size, utils::constraints::FOREIGN_KEY }};
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
[[maybe_unused]] attribute_definition make_fk_column(const std::string &name, const std::shared_ptr<attribute_definition> &ref_column)
|
||||
{
|
||||
return {name, utils::data_type_traits<Type>::type(0), 0, ref_column, { 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
|
||||
}
|
||||
|
||||
template <>
|
||||
[[maybe_unused]] attribute_definition make_fk_column<std::string>(const std::string &name, size_t size, const std::shared_ptr<attribute_definition> &ref_column);
|
||||
|
||||
template < typename Type >
|
||||
[[maybe_unused]] attribute_definition make_fk_column(const std::string &name, size_t size, const std::string &ref_table_name, const std::string &ref_column_name) {
|
||||
return {
|
||||
name, utils::data_type_traits<Type>::type(size), 0,
|
||||
std::make_shared<attribute_definition>(ref_column_name, ref_table_name, utils::data_type_traits<Type>::type(size), utils::constraints::FOREIGN_KEY),
|
||||
{ 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL
|
||||
};
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
[[maybe_unused]] attribute_definition make_fk_column(const std::string &name, const std::string &ref_table_name, const std::string &ref_column_name) {
|
||||
return {
|
||||
name, utils::data_type_traits<Type>::type(0), 0,
|
||||
std::make_shared<attribute_definition>(ref_column_name, ref_table_name, utils::data_type_traits<Type>::type(0), utils::constraints::FOREIGN_KEY),
|
||||
{ 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
[[maybe_unused]] attribute_definition make_fk_column<std::string>(const std::string &name, const std::string &ref_table_name, const std::string &ref_column_name);
|
||||
|
||||
template <>
|
||||
[[maybe_unused]] attribute_definition make_fk_column<std::string>(const std::string &name, size_t size, const std::string &ref_table_name, const std::string &ref_column_name);
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_COLUMN_DEFINITION_HPP
|
||||
@@ -1,140 +0,0 @@
|
||||
#ifndef QUERY_COLUMN_DEFINITION_GENERATOR_HPP
|
||||
#define QUERY_COLUMN_DEFINITION_GENERATOR_HPP
|
||||
|
||||
#include "attribute_definition.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/data_type_traits.hpp"
|
||||
#include "matador/utils/error.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
#include <typeindex>
|
||||
#include <vector>
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
class schema;
|
||||
|
||||
class fk_attribute_generator
|
||||
{
|
||||
public:
|
||||
fk_attribute_generator() = default;
|
||||
|
||||
template<class Type>
|
||||
attribute_definition generate(const char *id, Type &x, const std::shared_ptr<attribute_definition> &ref_column) {
|
||||
access::process(*this, x);
|
||||
return attribute_definition{id, type_, 0, ref_column, {utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void on_primary_key(const char *, ValueType &/*pk*/, std::enable_if_t<std::is_integral_v<ValueType> && !std::is_same_v<bool, ValueType>>* = nullptr) {
|
||||
type_ = utils::data_type_traits<ValueType>::type(0);
|
||||
}
|
||||
void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size);
|
||||
static void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
|
||||
template < class Type >
|
||||
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
static void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
template<class Pointer>
|
||||
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class Pointer>
|
||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
utils::basic_type type_{};
|
||||
};
|
||||
|
||||
class attribute_definition_generator final {
|
||||
private:
|
||||
attribute_definition_generator(std::vector<attribute_definition> &columns, const schema &repo);
|
||||
|
||||
public:
|
||||
~attribute_definition_generator() = default;
|
||||
|
||||
template < class Type >
|
||||
static std::vector<attribute_definition> generate(const schema &repo)
|
||||
{
|
||||
std::vector<attribute_definition> columns;
|
||||
attribute_definition_generator gen(columns, repo);
|
||||
Type obj;
|
||||
access::process(gen, obj);
|
||||
return columns;
|
||||
}
|
||||
|
||||
template < class V >
|
||||
void on_primary_key(const char *, V &x, std::enable_if_t<std::is_integral_v<V> && !std::is_same_v<bool, V>>* = nullptr);
|
||||
void on_primary_key(const char *id, std::string &pk, size_t size);
|
||||
void on_revision(const char *id, uint64_t &rev);
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes);
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr = utils::null_attributes);
|
||||
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) {
|
||||
on_foreign_key(id, x);
|
||||
}
|
||||
template<class Pointer>
|
||||
void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) {
|
||||
on_foreign_key(id, x);
|
||||
}
|
||||
|
||||
template <class Pointer>
|
||||
void on_foreign_key(const char *id, Pointer &x) {
|
||||
if (const auto result = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)))) {
|
||||
if (x.empty()) {
|
||||
typename Pointer::value_type temp_val;
|
||||
columns_.push_back(fk_column_generator_.generate(id, temp_val, *result));
|
||||
} else {
|
||||
columns_.push_back(fk_column_generator_.generate(id, *x, *result));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
template<class ContainerType>
|
||||
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
[[nodiscard]] utils::result<std::shared_ptr<attribute_definition>, utils::error> determine_foreign_ref(const std::type_index &ti) const;
|
||||
|
||||
private:
|
||||
size_t index_ = 0;
|
||||
std::vector<attribute_definition> &columns_;
|
||||
const schema &repo_;
|
||||
|
||||
fk_attribute_generator fk_column_generator_;
|
||||
};
|
||||
|
||||
template<typename V>
|
||||
void attribute_definition_generator::on_primary_key(const char *id, V &x, std::enable_if_t<std::is_integral_v<V> && !std::is_same_v<bool, V>>*)
|
||||
{
|
||||
on_attribute(id, x, { utils::constraints::PRIMARY_KEY });
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void attribute_definition_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &attr)
|
||||
{
|
||||
columns_.emplace_back(id, x, attr, null_option::NOT_NULL);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void attribute_definition_generator::on_attribute(const char *id, std::optional<Type> & /*x*/, const utils::field_attributes &attr)
|
||||
{
|
||||
columns_.emplace_back(id, utils::data_type_traits<Type>::type(attr.size()), attr, null_option::NULLABLE);
|
||||
}
|
||||
|
||||
}
|
||||
#endif //QUERY_COLUMN_DEFINITION_GENERATOR_HPP
|
||||
@@ -1,35 +1,69 @@
|
||||
#ifndef BASIC_PROTOTYPE_INFO_HPP
|
||||
#define BASIC_PROTOTYPE_INFO_HPP
|
||||
|
||||
#include "matador/object/object_definition.hpp"
|
||||
#include "matador/object/attribute.hpp"
|
||||
#include "matador/object/restriction.hpp"
|
||||
#include "matador/object/relation_endpoint.hpp"
|
||||
|
||||
#include "matador/utils/identifier.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
class schema_node;
|
||||
class repository_node;
|
||||
class relation_endpoint;
|
||||
|
||||
class basic_object_info {
|
||||
public:
|
||||
using t_endpoint_map = std::unordered_map<std::type_index, std::shared_ptr<relation_endpoint>>;
|
||||
using endpoint_iterator = t_endpoint_map::iterator;
|
||||
using const_endpoint_iterator = t_endpoint_map::const_iterator;
|
||||
|
||||
virtual ~basic_object_info() = default;
|
||||
|
||||
[[nodiscard]] std::type_index type_index() const;
|
||||
[[nodiscard]] std::string name() const;
|
||||
[[nodiscard]] const object_definition& definition() const;
|
||||
[[nodiscard]] std::shared_ptr<attribute_definition> reference_column() const;
|
||||
[[nodiscard]] std::shared_ptr<class object> object() const;
|
||||
[[nodiscard]] const std::vector<attribute>& attributes() const;
|
||||
[[nodiscard]] const std::list<restriction>& constraints() const;
|
||||
|
||||
[[nodiscard]] bool has_primary_key() const;
|
||||
[[nodiscard]] const utils::identifier& primary_key() const;
|
||||
[[nodiscard]] const attribute* primary_key_attribute() const;
|
||||
|
||||
void update_name(const std::string& name) const;
|
||||
|
||||
virtual void on_attach() const = 0;
|
||||
virtual void on_detach() const = 0;
|
||||
|
||||
endpoint_iterator register_relation_endpoint(const std::type_index &type, const std::shared_ptr<relation_endpoint> &endpoint);
|
||||
void unregister_relation_endpoint(const std::type_index &type);
|
||||
|
||||
[[nodiscard]] const_endpoint_iterator find_relation_endpoint(const std::type_index &type) const;
|
||||
endpoint_iterator find_relation_endpoint(const std::type_index &type);
|
||||
|
||||
[[nodiscard]] const_endpoint_iterator find_relation_endpoint(const std::string &field) const;
|
||||
endpoint_iterator find_relation_endpoint(const std::string &field);
|
||||
|
||||
endpoint_iterator endpoint_begin();
|
||||
[[nodiscard]] const_endpoint_iterator endpoint_begin() const;
|
||||
|
||||
endpoint_iterator endpoint_end();
|
||||
[[nodiscard]] const_endpoint_iterator endpoint_end() const;
|
||||
|
||||
[[nodiscard]] std::size_t endpoints_size() const;
|
||||
[[nodiscard]] bool endpoints_empty() const;
|
||||
|
||||
protected:
|
||||
basic_object_info(std::shared_ptr<schema_node> node, std::type_index type_index, utils::identifier &&pk, std::shared_ptr<attribute_definition> &&pk_column, object_definition &&definition);
|
||||
basic_object_info(std::shared_ptr<schema_node> node, std::type_index type_index, utils::identifier &&pk, std::shared_ptr<attribute_definition> &&pk_column);
|
||||
basic_object_info(const repository_node& node, const std::shared_ptr<class object> &obj);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<schema_node> node_; /**< prototype node of the represented object type */
|
||||
std::type_index type_index_; /**< type index of the represented object type */
|
||||
object_definition definition_;
|
||||
std::optional<utils::identifier> identifier_;
|
||||
std::shared_ptr<attribute_definition> pk_column_;
|
||||
std::shared_ptr<class object> object_;
|
||||
const repository_node& node_; /**< prototype node of the represented object type */
|
||||
t_endpoint_map relation_endpoints_;
|
||||
};
|
||||
|
||||
using basic_object_info_ref = std::reference_wrapper<const basic_object_info>;
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
#ifndef MATADOR_BASIC_REPOSITORY_HPP
|
||||
#define MATADOR_BASIC_REPOSITORY_HPP
|
||||
|
||||
#include "matador/object/error_code.hpp"
|
||||
#include "matador/object/object_info.hpp"
|
||||
#include "matador/object/repository_node_iterator.hpp"
|
||||
|
||||
#include "matador/logger/logger.hpp"
|
||||
|
||||
#include "matador/utils/error.hpp"
|
||||
#include "matador/utils/result.hpp"
|
||||
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace matador::object {
|
||||
utils::error make_error(error_code ec, const std::string &msg);
|
||||
|
||||
class repository_node;
|
||||
class const_repository_node_iterator;
|
||||
|
||||
class basic_repository {
|
||||
public:
|
||||
typedef const_repository_node_iterator const_iterator; /**< Shortcut for the list const iterator. */
|
||||
|
||||
explicit basic_repository(std::string name = "");
|
||||
~basic_repository();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node Node to attach
|
||||
* @param parent Name of parent node
|
||||
* @return Attached node
|
||||
*/
|
||||
[[nodiscard]] utils::result<repository_node*, utils::error> attach_node(repository_node *node, const std::string &parent);
|
||||
|
||||
/**
|
||||
* Detaches a given node from the schema. If the
|
||||
* node is a parent of other nodes, these nodes are
|
||||
* detached as well.
|
||||
*
|
||||
* @param node Node to detach from schema
|
||||
* @return Result object indicating success or failure
|
||||
*/
|
||||
[[nodiscard]] utils::result<void, utils::error> detach(repository_node *node);
|
||||
|
||||
/**
|
||||
* Return the first schema node.
|
||||
*
|
||||
* @return The first schema node iterator.
|
||||
*/
|
||||
[[nodiscard]] const_iterator begin() const;
|
||||
|
||||
/**
|
||||
* Return the last schema node.
|
||||
*
|
||||
* @return The last schema node iterator.
|
||||
*/
|
||||
[[nodiscard]] const_iterator end() const;
|
||||
|
||||
/**
|
||||
* Returns true if the schema contains
|
||||
* no schema nodes.
|
||||
*
|
||||
* @return True if the schema is empty
|
||||
*/
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
||||
/**
|
||||
* Returns the current number of the schema node.
|
||||
*
|
||||
* @return Number of schema nodes
|
||||
*/
|
||||
[[nodiscard]] size_t size() const;
|
||||
|
||||
/**
|
||||
* Returns the name of the schema.
|
||||
*
|
||||
* @return The name of the schema
|
||||
*/
|
||||
[[nodiscard]] std::string name() const;
|
||||
|
||||
[[nodiscard]] bool contains(const std::string &name) const;
|
||||
[[nodiscard]] bool contains(const std::type_index &index) const;
|
||||
template < typename Type >
|
||||
[[nodiscard]] bool contains() const {
|
||||
return contains(std::type_index(typeid(Type)));
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] utils::result<basic_object_info_ref, utils::error> basic_info(const std::type_index& ti) const;
|
||||
[[nodiscard]] utils::result<basic_object_info_ref, utils::error> basic_info(const std::string &name) const;
|
||||
template<typename Type>
|
||||
[[nodiscard]] utils::result<basic_object_info_ref, utils::error> basic_info() const {
|
||||
return basic_info(std::type_index(typeid(Type)));
|
||||
}
|
||||
|
||||
[[nodiscard]] utils::result<const attribute*, utils::error> primary_key_attribute(const std::type_index &ti) const;
|
||||
|
||||
void dump(std::ostream &os) const;
|
||||
static void dump(std::ostream &os, const repository_node& node);
|
||||
|
||||
protected:
|
||||
using t_node_map = std::unordered_map<std::string, repository_node*>;
|
||||
using t_type_index_node_map = std::unordered_map<std::type_index, repository_node*>;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] const_iterator find_node(const std::string &name) const;
|
||||
[[nodiscard]] const_iterator find_node(const std::type_index &type_index) const;
|
||||
template<typename Type>
|
||||
[[nodiscard]] const_iterator find_node() const {
|
||||
return find_node(std::type_index(typeid(Type)));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool has_node(const std::string &name) const;
|
||||
[[nodiscard]] bool has_node(const std::type_index &index) const;
|
||||
[[nodiscard]] bool has_node(const repository_node* node) const;
|
||||
|
||||
static void insert_node(repository_node *parent, repository_node *child);
|
||||
void remove_node(repository_node *node);
|
||||
|
||||
[[nodiscard]] bool expecting_relation_node(const std::string &name) const;
|
||||
void expect_relation_node(const std::string &name, const std::type_index &ti);
|
||||
void remove_expected_relation_node(const std::string &name);
|
||||
|
||||
[[nodiscard]] std::shared_ptr<object> provide_object_in_advance(const std::type_index &ti, const std::shared_ptr<object>& obj);
|
||||
[[nodiscard]] bool has_object_for_type(const std::type_index &ti) const;
|
||||
[[nodiscard]] std::shared_ptr<object> object_for_type(const std::type_index &ti) const;
|
||||
void remove_object_for_type(const std::type_index &ti);
|
||||
|
||||
[[nodiscard]] bool is_node_announced(const std::type_index &ti) const;
|
||||
void push_announce_node(const std::type_index &ti, std::unique_ptr<repository_node> &&node);
|
||||
[[nodiscard]] repository_node* announce_node(const std::type_index &ti) const;
|
||||
[[nodiscard]] std::unique_ptr<repository_node> pop_announce_node(const std::type_index &ti);
|
||||
|
||||
protected:
|
||||
friend class object_generator;
|
||||
template < typename NodeType, template<typename> typename ...Observers >
|
||||
friend class foreign_node_completer;
|
||||
template < typename NodeType, template<typename> typename ...Observers >
|
||||
friend class relation_completer;
|
||||
|
||||
std::string name_;
|
||||
repository_node* root_{nullptr};
|
||||
|
||||
t_node_map nodes_by_name_;
|
||||
t_type_index_node_map nodes_by_type_;
|
||||
logger::logger log_;
|
||||
|
||||
std::unordered_map<std::type_index, std::unique_ptr<repository_node>> announced_node_;
|
||||
std::unordered_map<std::type_index, attribute*> missing_references_{};
|
||||
std::unordered_map<std::string, std::type_index> expected_relation_nodes_;
|
||||
std::unordered_map<std::type_index, std::shared_ptr<object>> object_by_type_{};
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_BASIC_REPOSITORY_HPP
|
||||
@@ -1,16 +1,59 @@
|
||||
#ifndef COLLECTION_HPP
|
||||
#define COLLECTION_HPP
|
||||
|
||||
#include <vector>
|
||||
#include "matador/object/collection_proxy.hpp"
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
template < class Type >
|
||||
class collection {
|
||||
public:
|
||||
using value_type = typename collection_proxy<Type>::value_type;
|
||||
using iterator = typename collection_proxy<Type>::iterator;
|
||||
using const_iterator = typename collection_proxy<Type>::const_iterator;
|
||||
|
||||
collection()
|
||||
: proxy_(std::make_shared<collection_proxy<Type>>()) {}
|
||||
explicit collection(std::shared_ptr<collection_proxy<Type>> proxy)
|
||||
: proxy_(std::move(proxy)) {}
|
||||
explicit collection(std::vector<Type> items)
|
||||
: proxy_(std::make_shared<collection_proxy<Type>>(std::move(items))) {}
|
||||
collection(const collection& other) = default;
|
||||
|
||||
void push_back(const value_type& value) {
|
||||
proxy_->push_back(value);
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return proxy_->begin();
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return proxy_->end();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return proxy_->begin();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return proxy_->end();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t size() const {
|
||||
return proxy_->size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return proxy_->empty();
|
||||
}
|
||||
|
||||
void reset(std::shared_ptr<collection_proxy<Type>> proxy) {
|
||||
proxy_ = std::move(proxy);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<abstract_collection_proxy<Type>> proxy_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
#ifndef MATADOR_COLLECTION_PROXY_HPP
|
||||
#define MATADOR_COLLECTION_PROXY_HPP
|
||||
|
||||
#include "matador/object/collection_resolver.hpp"
|
||||
#include "matador/object/many_to_many_relation.hpp"
|
||||
|
||||
#include "matador/utils/identifier.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
// has many primitive
|
||||
// relation<OwnerType, PrimitiveType>
|
||||
// has many ptr
|
||||
// relation<OwnerType, ObjectType>
|
||||
// has many to many
|
||||
// relation<OwnerType, ForeignType>
|
||||
|
||||
template < class RelationType >
|
||||
struct relation_iterator_traits {
|
||||
static RelationType& value(RelationType& item) {
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
template < typename Type, typename OwnerType >
|
||||
struct relation_iterator_traits<relation<OwnerType, Type>> {
|
||||
static Type& value(relation<OwnerType, Type>& re) {
|
||||
return re.relation().value();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type, typename RelationType = Type>
|
||||
class collection_proxy_iterator {
|
||||
public:
|
||||
using value_type = Type;
|
||||
using reference = Type&;
|
||||
using pointer = Type*;
|
||||
using relation_type = RelationType;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
collection_proxy_iterator() = default;
|
||||
collection_proxy_iterator(typename std::vector<RelationType>::iterator it)
|
||||
: it_(it) {}
|
||||
|
||||
reference operator*() {
|
||||
return relation_iterator_traits<relation_type>::value(*it_);
|
||||
}
|
||||
|
||||
pointer operator->() {
|
||||
return &relation_iterator_traits<relation_type>::value(*it_);
|
||||
}
|
||||
|
||||
collection_proxy_iterator& operator++() {
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const collection_proxy_iterator& other) const {
|
||||
return it_ == other.it_;
|
||||
}
|
||||
bool operator!=(const collection_proxy_iterator& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
// relation_type& relation() {
|
||||
|
||||
// return *it_;
|
||||
// }
|
||||
private:
|
||||
typename std::vector<RelationType>::iterator it_;
|
||||
};
|
||||
|
||||
template<typename Type, typename RelationType = Type>
|
||||
class abstract_collection_proxy {
|
||||
public:
|
||||
using value_type = Type;
|
||||
using relation_type = RelationType;
|
||||
// using iterator = typename std::vector<value_type>::iterator;
|
||||
// using const_iterator = typename std::vector<value_type>::const_iterator;
|
||||
using iterator = collection_proxy_iterator<value_type, relation_type>;
|
||||
using const_iterator = collection_proxy_iterator<value_type, relation_type>;
|
||||
|
||||
virtual ~abstract_collection_proxy() = default;
|
||||
|
||||
virtual void push_back(const value_type& value) = 0;
|
||||
|
||||
virtual iterator begin() = 0;
|
||||
virtual iterator end() = 0;
|
||||
|
||||
[[nodiscard]] virtual size_t size() = 0;
|
||||
[[nodiscard]] virtual bool empty() = 0;
|
||||
|
||||
[[nodiscard]] virtual const utils::identifier& owner_id() const = 0;
|
||||
|
||||
protected:
|
||||
std::vector<relation_type> items_;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class collection_proxy : public abstract_collection_proxy<Type> {
|
||||
public:
|
||||
using value_type = Type;
|
||||
using iterator = typename abstract_collection_proxy<Type>::iterator;
|
||||
using const_iterator = typename abstract_collection_proxy<Type>::const_iterator;
|
||||
|
||||
collection_proxy() = default;
|
||||
|
||||
// Lazy
|
||||
collection_proxy(std::weak_ptr<collection_resolver<Type>> resolver, utils::identifier owner_id)
|
||||
: owner_id_(std::move(owner_id)), resolver_(std::move(resolver)) {}
|
||||
|
||||
// Eager
|
||||
collection_proxy(std::weak_ptr<collection_resolver<Type>> resolver, std::vector<Type> items)
|
||||
: loaded_{true}, items_(std::move(items)), resolver_(std::move(resolver)) {}
|
||||
|
||||
// Transient
|
||||
explicit collection_proxy(std::vector<Type> items)
|
||||
: items_(std::move(items)) {}
|
||||
|
||||
[[nodiscard]] const utils::identifier& owner_id() const override {
|
||||
return owner_id_;
|
||||
}
|
||||
|
||||
iterator begin() override {
|
||||
resolve();
|
||||
return items_.begin();
|
||||
}
|
||||
iterator end() override {
|
||||
resolve();
|
||||
return items_.end();
|
||||
}
|
||||
|
||||
void push_back(const value_type& value) override {
|
||||
resolve();
|
||||
items_.push_back(value);
|
||||
}
|
||||
[[nodiscard]] size_t size() override {
|
||||
resolve();
|
||||
return items_.size();
|
||||
}
|
||||
[[nodiscard]] bool empty() override {
|
||||
resolve();
|
||||
return items_.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
void resolve() {
|
||||
if (loaded_) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex_);
|
||||
auto resolver = resolver_.lock();
|
||||
if (!resolver) {
|
||||
return;
|
||||
// Todo: Add states (Detached, Attached, Transient) - if attached an no resolver is available throw runtime exception
|
||||
// throw std::runtime_error("Detached proxy (session expired)");
|
||||
}
|
||||
|
||||
items_ = resolver->resolve(owner_id_);
|
||||
loaded_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
const utils::identifier owner_id_;
|
||||
std::atomic_bool loaded_{false};
|
||||
std::vector<Type> items_;
|
||||
std::weak_ptr<collection_resolver<Type>> resolver_{};
|
||||
mutable std::mutex mutex_{};
|
||||
};
|
||||
|
||||
template<typename Type, class OwnerType>
|
||||
class collection_many_to_many_proxy {
|
||||
public:
|
||||
using value_type = Type;
|
||||
using owner_type = OwnerType;
|
||||
using relation_type = many_to_many_relation<value_type, owner_type>;
|
||||
using iterator = typename std::vector<relation_type>::iterator;
|
||||
using const_iterator = typename std::vector<relation_type>::const_iterator;
|
||||
|
||||
private:
|
||||
std::vector<relation_type> relations_;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_COLLECTION_PROXY_HPP
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef MATADOR_COLLECTION_RESOLVER_HPP
|
||||
#define MATADOR_COLLECTION_RESOLVER_HPP
|
||||
|
||||
#include "matador/object/abstract_joined_resolver.hpp"
|
||||
|
||||
#include <typeindex>
|
||||
#include <vector>
|
||||
|
||||
namespace matador::utils {
|
||||
class identifier;
|
||||
}
|
||||
|
||||
namespace matador::object {
|
||||
template<typename Type>
|
||||
class collection_resolver : public abstract_joined_resolver {
|
||||
public:
|
||||
collection_resolver(const std::type_index& root_type, std::string collection_name)
|
||||
: abstract_joined_resolver(root_type, typeid(Type), std::move(collection_name)) {}
|
||||
|
||||
virtual std::vector<Type> resolve(const utils::identifier& id) = 0;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_COLLECTION_RESOLVER_HPP
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef MATADOR_COLLECTION_UTILS_HPP
|
||||
#define MATADOR_COLLECTION_UTILS_HPP
|
||||
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
|
||||
namespace matador::object {
|
||||
struct collection_composite_key {
|
||||
std::type_index root_type;
|
||||
std::type_index type;
|
||||
std::string name;
|
||||
|
||||
bool operator==(const collection_composite_key& other) const;
|
||||
};
|
||||
|
||||
struct collection_composite_key_hash {
|
||||
std::size_t operator()(const collection_composite_key& k) const noexcept;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_COLLECTION_UTILS_HPP
|
||||
@@ -10,6 +10,7 @@ enum class error_code : uint8_t {
|
||||
OK = 0,
|
||||
NodeNotFound = 1,
|
||||
NodeAlreadyExists = 2,
|
||||
NoPrimaryKey = 3,
|
||||
Failure,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
#ifndef FOREIGN_NODE_COMPLETER_HPP
|
||||
#define FOREIGN_NODE_COMPLETER_HPP
|
||||
|
||||
#include "matador/object/basic_repository.hpp"
|
||||
#include "matador/object/internal/observer_list_copy_creator.hpp"
|
||||
#include "matador/object/repository_node.hpp"
|
||||
#include "matador/object/join_columns_collector.hpp"
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/utils/primary_key_attribute.hpp"
|
||||
|
||||
#include "matador/logger/log_manager.hpp"
|
||||
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
namespace matador::object {
|
||||
template<typename NodeType, template<typename> typename... Observers>
|
||||
class completer {
|
||||
public:
|
||||
template<typename Type>
|
||||
void foo(Observers<NodeType>&&... observers) {
|
||||
(std::unique_ptr<Observers<Type>>(new Observers(std::forward<Observers>(observers))), ...);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes the given node and ensures
|
||||
* that all foreign nodes needed by the given node
|
||||
* relations are attached in schema.
|
||||
*/
|
||||
template<typename NodeType, template<typename> typename ...Observers>
|
||||
class foreign_node_completer final {
|
||||
public:
|
||||
static void complete(repository_node *node, const std::vector<std::unique_ptr<observer<NodeType>>> &observers) {
|
||||
foreign_node_completer completer(node->repo_, observers);
|
||||
|
||||
completer.complete_node(node);
|
||||
}
|
||||
|
||||
template<typename BaseType>
|
||||
static void on_base(const BaseType&) {}
|
||||
template<class PrimaryKeyType>
|
||||
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute & /*attr*/) {}
|
||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||
template<typename AttributeType>
|
||||
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/) {}
|
||||
template<typename AttributeType>
|
||||
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/) {}
|
||||
template<class ForeignPointerType>
|
||||
void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
||||
template<class ForeignPointerType>
|
||||
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char *join_column, const utils::foreign_attributes &/*attr*/);
|
||||
template<class CollectionType>
|
||||
void on_has_many(const char *id, CollectionType &,
|
||||
const char *join_column,
|
||||
const utils::foreign_attributes &attr,
|
||||
std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * = nullptr);
|
||||
|
||||
template<class CollectionType>
|
||||
void on_has_many(const char * /*id*/,
|
||||
CollectionType &,
|
||||
const char * /*join_column*/,
|
||||
const utils::foreign_attributes & /*attr*/,
|
||||
std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
|
||||
}
|
||||
|
||||
template<class CollectionType>
|
||||
static void on_has_many_to_many(const char * /*id*/,
|
||||
CollectionType &/*collection*/,
|
||||
const char * /*join_column*/,
|
||||
const char * /*inverse_join_column*/,
|
||||
const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class CollectionType>
|
||||
static void on_has_many_to_many(const char * /*id*/, CollectionType &/*collection*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
explicit foreign_node_completer(basic_repository &repo, const std::vector<std::unique_ptr<observer<NodeType>>> &observers)
|
||||
: repo_(repo)
|
||||
, log_(logger::create_logger("node_completer"))
|
||||
, observers_(observers) {}
|
||||
|
||||
void complete_node(repository_node *node) {
|
||||
nodes_.push(node);
|
||||
|
||||
NodeType obj;
|
||||
access::process(*this, obj);
|
||||
nodes_.pop();
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void attach_node() {
|
||||
if (repo_.contains(typeid(Type))) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto observers = internal::observer_list_copy_creator<NodeType, Type, Observers...>::copy_create(observers_);
|
||||
|
||||
if (repo_.is_node_announced(typeid(Type))) {
|
||||
return;
|
||||
}
|
||||
auto node = repository_node::make_node<Type>(repo_, "", []{ return std::make_unique<Type>(); }, std::move(observers));
|
||||
repo_.push_announce_node(typeid(Type), std::move(node));
|
||||
// if (auto result = repo_.attach_node(node)) {
|
||||
// foreign_node_completer<Type>::complete(result.value(), {});
|
||||
// }
|
||||
}
|
||||
|
||||
private:
|
||||
using endpoint_ptr = std::shared_ptr<relation_endpoint>;
|
||||
static void register_relation_endpoints(const endpoint_ptr &endpoint,
|
||||
const endpoint_ptr &other_endpoint);
|
||||
static void link_relation_endpoints(const endpoint_ptr &endpoint,
|
||||
const endpoint_ptr &other_endpoint);
|
||||
|
||||
private:
|
||||
template<typename Type, template<typename> typename ...OtherObservers>
|
||||
friend class foreign_node_completer;
|
||||
|
||||
private:
|
||||
std::stack<repository_node*> nodes_;
|
||||
basic_repository &repo_;
|
||||
logger::logger log_;
|
||||
join_columns_collector join_columns_collector_{};
|
||||
const std::vector<std::unique_ptr<observer<NodeType>>>& observers_;
|
||||
};
|
||||
|
||||
|
||||
template<typename NodeType, template<typename> typename ...Observers>
|
||||
template<class ForeignPointerType>
|
||||
void foreign_node_completer<NodeType, Observers...>::on_belongs_to(const char * /*id*/, ForeignPointerType &, const utils::foreign_attributes &) {
|
||||
attach_node<typename ForeignPointerType::value_type>();
|
||||
}
|
||||
|
||||
template<typename NodeType, template<typename> typename ...Observers>
|
||||
template<class ForeignPointerType>
|
||||
void foreign_node_completer<NodeType, Observers...>::on_has_one(const char * /*id*/, ForeignPointerType &, const char * /*join_column*/, const utils::foreign_attributes &) {
|
||||
attach_node<typename ForeignPointerType::value_type>();
|
||||
}
|
||||
|
||||
template<typename NodeType, template<typename> typename ...Observers>
|
||||
template<class CollectionType>
|
||||
void foreign_node_completer<NodeType, Observers...>::on_has_many(const char * /*id*/,
|
||||
CollectionType &, const char * /*join_column*/,
|
||||
const utils::foreign_attributes & /*attr*/,
|
||||
std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> *) {
|
||||
attach_node<typename CollectionType::value_type::value_type>();
|
||||
}
|
||||
|
||||
template<typename NodeType, template<typename> typename ...Observers>
|
||||
void foreign_node_completer<NodeType, Observers...>::register_relation_endpoints(const endpoint_ptr &endpoint,
|
||||
const endpoint_ptr &other_endpoint) {
|
||||
endpoint->node().info_->register_relation_endpoint(other_endpoint->node().type_index(), endpoint);
|
||||
other_endpoint->node().info_->register_relation_endpoint(endpoint->node().type_index(), other_endpoint);
|
||||
link_relation_endpoints(endpoint, other_endpoint);
|
||||
}
|
||||
|
||||
template<typename NodeType, template<typename> typename ...Observers>
|
||||
void foreign_node_completer<NodeType, Observers...>::link_relation_endpoints(const endpoint_ptr &endpoint, const endpoint_ptr &other_endpoint) {
|
||||
endpoint->link_foreign_endpoint(other_endpoint);
|
||||
other_endpoint->link_foreign_endpoint(endpoint);
|
||||
}
|
||||
}
|
||||
#endif //FOREIGN_NODE_COMPLETER_HPP
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef MATADOR_OBSERVER_LIST_COPY_CREATOR_HPP
|
||||
#define MATADOR_OBSERVER_LIST_COPY_CREATOR_HPP
|
||||
|
||||
#include "matador/object/observer.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace matador::object::internal {
|
||||
template <typename SourceType, typename DestType, template <typename> class... ObserverType>
|
||||
class observer_list_copy_creator
|
||||
{
|
||||
public:
|
||||
using source_observer_vector = std::vector<std::unique_ptr<observer<SourceType>>>;
|
||||
using observer_vector = std::vector<std::unique_ptr<observer<DestType>>>;
|
||||
using iterator = typename observer_vector::iterator;
|
||||
|
||||
static observer_vector copy_create(const source_observer_vector &source_observers) {
|
||||
observer_list_copy_creator creator(source_observers);
|
||||
|
||||
return std::move(creator.observers_);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit observer_list_copy_creator(const source_observer_vector &source_observers)
|
||||
: source_observers_(source_observers) {
|
||||
if constexpr (sizeof...(ObserverType) != 0) {
|
||||
copy_observer<ObserverType...>();
|
||||
}
|
||||
}
|
||||
|
||||
template <template <typename> class FirstObserverType>
|
||||
void copy_observer() {
|
||||
try_copy_observer<FirstObserverType>();
|
||||
}
|
||||
|
||||
template <template <typename> class FirstObserverType, template <typename> class NextObserverType, template <typename> class... RestObserverType>
|
||||
void copy_observer() {
|
||||
try_copy_observer<FirstObserverType>();
|
||||
copy_observer<NextObserverType, RestObserverType...>();
|
||||
}
|
||||
|
||||
template <template <typename> class CurrentObserverType>
|
||||
void try_copy_observer() {
|
||||
for ( const auto &obs : source_observers_ ) {;
|
||||
if (const auto *casted_observer = dynamic_cast<const CurrentObserverType<SourceType>*>(obs.get()); casted_observer != nullptr) {
|
||||
observers_.emplace_back(std::make_unique<CurrentObserverType<DestType>>(*casted_observer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
const source_observer_vector &source_observers_;
|
||||
observer_vector observers_;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_OBSERVER_LIST_COPY_CREATOR_HPP
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef MATADOR_OBSERVER_LIST_CREATOR_HPP
|
||||
#define MATADOR_OBSERVER_LIST_CREATOR_HPP
|
||||
|
||||
#include "matador/object/observer.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace matador::object::internal {
|
||||
template <typename Type, template <typename> class... ObserverType>
|
||||
class observer_list_creator
|
||||
{
|
||||
public:
|
||||
using observer_vector = std::vector<std::unique_ptr<observer<Type>>>;
|
||||
|
||||
static void create_missing(observer_vector &observers) {
|
||||
observer_list_creator creator(observers);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit observer_list_creator(observer_vector &observers)
|
||||
: observers_(observers) {
|
||||
if constexpr (sizeof...(ObserverType) != 0) {
|
||||
build_observer<ObserverType...>();
|
||||
}
|
||||
}
|
||||
|
||||
template <template <typename> class FirstObserverType>
|
||||
void build_observer() {
|
||||
try_copy_on_missing<FirstObserverType>();
|
||||
}
|
||||
|
||||
template <template <typename> class FirstObserverType, template <typename> class NextObserverType, template <typename> class... RestObserverType>
|
||||
void build_observer() {
|
||||
try_copy_on_missing<FirstObserverType>();
|
||||
build_observer<NextObserverType, RestObserverType...>();
|
||||
}
|
||||
|
||||
template <template <typename> class CurrentObserverType>
|
||||
void try_copy_on_missing() {
|
||||
bool is_missing{true};
|
||||
for ( const auto &obs : observers_ ) {;
|
||||
if (dynamic_cast<const CurrentObserverType<Type>*>(obs.get())) {
|
||||
is_missing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_missing) {
|
||||
append<CurrentObserverType<Type>>();
|
||||
}
|
||||
}
|
||||
|
||||
template <class CurrentObserverType>
|
||||
void append(std::enable_if_t<std::is_default_constructible_v<CurrentObserverType> || std::is_trivially_default_constructible_v<CurrentObserverType>>* = nullptr) {
|
||||
observers_.emplace_back(std::make_unique<CurrentObserverType>());
|
||||
}
|
||||
|
||||
template <class CurrentObserverType>
|
||||
void append(std::enable_if_t<!std::is_default_constructible_v<CurrentObserverType> && !std::is_trivially_default_constructible_v<CurrentObserverType>>* = nullptr) {
|
||||
static_assert("no default constructor");
|
||||
}
|
||||
|
||||
private:
|
||||
observer_vector &observers_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_OBSERVER_LIST_CREATOR_HPP
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef JOIN_COLUMNS_COLLECTOR_HPP
|
||||
#define JOIN_COLUMNS_COLLECTOR_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
#include "matador/utils/primary_key_attribute.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::object {
|
||||
struct join_columns {
|
||||
std::string join_column;
|
||||
std::string inverse_join_column;
|
||||
};
|
||||
|
||||
class join_columns_collector final {
|
||||
public:
|
||||
template<class Type>
|
||||
join_columns collect() {
|
||||
join_columns_ = {};
|
||||
Type obj;
|
||||
|
||||
access::process(*this, obj);
|
||||
|
||||
return join_columns_;
|
||||
}
|
||||
template<typename BaseType>
|
||||
static void on_base(const BaseType&) {}
|
||||
template < class V >
|
||||
static void on_primary_key(const char * /*id*/, V &, const utils::primary_key_attribute& /*attr*/) {}
|
||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||
template<typename Type>
|
||||
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/) {}
|
||||
template<class Pointer>
|
||||
static void on_belongs_to(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class Pointer>
|
||||
static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many(ContainerType &, const char */*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {
|
||||
join_columns_.join_column = join_column;
|
||||
join_columns_.inverse_join_column = inverse_join_column;
|
||||
}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
join_columns join_columns_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //JOIN_COLUMNS_COLLECTOR_HPP
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
|
||||
#define MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
|
||||
|
||||
#include "matador/object/collection_resolver.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::object {
|
||||
class abstract_collection_resolver_factory {
|
||||
public:
|
||||
virtual ~abstract_collection_resolver_factory() = default;
|
||||
|
||||
[[nodiscard]] virtual std::shared_ptr<abstract_joined_resolver> acquire_collection_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0;
|
||||
virtual void register_collection_resolver(std::shared_ptr<abstract_joined_resolver> &&resolver) = 0;
|
||||
};
|
||||
|
||||
class joined_collection_resolver_factory : public abstract_collection_resolver_factory {
|
||||
public:
|
||||
template<class Type>
|
||||
[[nodiscard]] std::shared_ptr<collection_resolver<Type>> resolver(const std::type_index &root_type, const std::string &collection_name) const {
|
||||
const auto res = acquire_collection_resolver(root_type, typeid(Type), collection_name);
|
||||
if (!res) {
|
||||
return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
|
||||
}
|
||||
|
||||
return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
|
||||
}
|
||||
};
|
||||
|
||||
class abstract_joined_object_resolver_factory {
|
||||
public:
|
||||
virtual ~abstract_joined_object_resolver_factory() = default;
|
||||
|
||||
[[nodiscard]] virtual std::shared_ptr<abstract_type_resolver> acquire_joined_object_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0;
|
||||
virtual void register_joined_object_resolver(std::shared_ptr<abstract_type_resolver> &&resolver, const std::type_index& root_type, const std::string& join_column) = 0;
|
||||
};
|
||||
|
||||
class joined_object_resolver_factory : public abstract_joined_object_resolver_factory {
|
||||
public:
|
||||
template<class Type>
|
||||
[[nodiscard]] std::shared_ptr<object_resolver<Type>> resolver(const std::type_index &root_type, const std::string &collection_name) const {
|
||||
const auto res = acquire_joined_object_resolver(root_type, typeid(Type), collection_name);
|
||||
if (!res) {
|
||||
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
|
||||
}
|
||||
|
||||
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
|
||||
@@ -8,6 +8,48 @@
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
template < class LocalType, class ForeignType >
|
||||
class relation {
|
||||
public:
|
||||
relation() = default;
|
||||
relation(std::string local_name, std::string remote_name)
|
||||
: local_name_(std::move(local_name))
|
||||
, remote_name_(std::move(remote_name)) {}
|
||||
relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const ForeignType& remote)
|
||||
: local_name_(std::move(local_name))
|
||||
, remote_name_(std::move(remote_name))
|
||||
, local_(local)
|
||||
, remote_(remote)
|
||||
{}
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::access;
|
||||
field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy);
|
||||
foreign_field(op);
|
||||
}
|
||||
|
||||
object_ptr<LocalType> local() const { return local_; }
|
||||
const ForeignType& remote() const { return remote_; }
|
||||
|
||||
private:
|
||||
template<typename Operator, class RemoteType = ForeignType>
|
||||
void foreign_field(Operator &op, std::enable_if_t<!is_object_ptr<RemoteType>::value>* /*unused*/) {
|
||||
namespace field = matador::access;
|
||||
field::attribute(op, remote_name_.c_str(), remote_);
|
||||
}
|
||||
template<typename Operator, class RemoteType = ForeignType>
|
||||
void foreign_field(Operator &op, std::enable_if_t<is_object_ptr<RemoteType>::value>* /*unused*/) {
|
||||
namespace field = matador::access;
|
||||
field::belongs_to(op, remote_name_.c_str(), remote_, utils::CascadeNoneFetchLazy);
|
||||
}
|
||||
private:
|
||||
std::string local_name_;
|
||||
std::string remote_name_;
|
||||
object_ptr<LocalType> local_;
|
||||
ForeignType remote_;
|
||||
};
|
||||
|
||||
template < class LocalType, class ForeignType >
|
||||
class many_to_many_relation {
|
||||
public:
|
||||
@@ -15,12 +57,18 @@ public:
|
||||
many_to_many_relation(std::string local_name, std::string remote_name)
|
||||
: local_name_(std::move(local_name))
|
||||
, remote_name_(std::move(remote_name)) {}
|
||||
many_to_many_relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const object_ptr<ForeignType>& remote)
|
||||
: local_name_(std::move(local_name))
|
||||
, remote_name_(std::move(remote_name))
|
||||
, local_(local)
|
||||
, remote_(remote)
|
||||
{}
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::access;
|
||||
field::belongs_to(op, local_name_.c_str(), local_, utils::default_foreign_attributes);
|
||||
field::belongs_to(op, remote_name_.c_str(), remote_, utils::default_foreign_attributes);
|
||||
field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy);
|
||||
field::belongs_to(op, remote_name_.c_str(), remote_, utils::CascadeNoneFetchLazy);
|
||||
}
|
||||
|
||||
object_ptr<LocalType> local() const { return local_; }
|
||||
@@ -33,5 +81,35 @@ private:
|
||||
object_ptr<ForeignType> remote_;
|
||||
};
|
||||
|
||||
template < class LocalType, class Type >
|
||||
class many_to_relation {
|
||||
public:
|
||||
many_to_relation() = default;
|
||||
many_to_relation(std::string local_name, std::string remote_name)
|
||||
: local_name_(std::move(local_name))
|
||||
, type_name_(std::move(remote_name)) {}
|
||||
many_to_relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const Type& value)
|
||||
: local_name_(std::move(local_name))
|
||||
, type_name_(std::move(remote_name))
|
||||
, local_(local)
|
||||
, value_(value){}
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
namespace field = matador::access;
|
||||
field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy);
|
||||
field::attribute(op, type_name_.c_str(), value_);
|
||||
}
|
||||
|
||||
object_ptr<LocalType> local() const { return local_; }
|
||||
Type value() const { return value_; }
|
||||
|
||||
private:
|
||||
std::string local_name_;
|
||||
std::string type_name_;
|
||||
object_ptr<LocalType> local_;
|
||||
Type value_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_HAS_MANY_TO_MANY_RELATION_HPP
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef MATADOR_OBJECT_HPP
|
||||
#define MATADOR_OBJECT_HPP
|
||||
|
||||
#include "matador/object/attribute.hpp"
|
||||
#include "matador/object/restriction.hpp"
|
||||
|
||||
#include "matador/utils/identifier.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <ostream>
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
class object {
|
||||
public:
|
||||
explicit object(std::string name);
|
||||
|
||||
static const attribute& create_attribute(std::string name, const std::shared_ptr<object>& obj);
|
||||
|
||||
[[nodiscard]] const attribute* primary_key_attribute() const;
|
||||
[[nodiscard]] const utils::identifier& primary_key() const;
|
||||
[[nodiscard]] bool has_primary_key() const;
|
||||
|
||||
[[nodiscard]] bool is_relation_object() const;
|
||||
[[nodiscard]] const attribute* join_attribute() const;
|
||||
[[nodiscard]] const attribute* inverse_join_attribute() const;
|
||||
|
||||
[[nodiscard]] const std::string& name() const;
|
||||
|
||||
void update_name(const std::string& name);
|
||||
|
||||
[[nodiscard]] bool has_attributes() const;
|
||||
[[nodiscard]] size_t attribute_count() const;
|
||||
[[nodiscard]] const std::vector<attribute>& attributes() const;
|
||||
|
||||
[[nodiscard]] bool has_constraints() const;
|
||||
[[nodiscard]] size_t constraint_count() const;
|
||||
[[nodiscard]] const std::list<restriction>& constraints() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const object& obj);
|
||||
|
||||
private:
|
||||
friend class attribute_generator;
|
||||
friend class object_generator;
|
||||
|
||||
std::string name_;
|
||||
int pk_column_index_{-1};
|
||||
int join_column_index_{-1};
|
||||
int inverse_join_column_index_{-1};
|
||||
|
||||
utils::identifier pk_identifier_;
|
||||
std::vector<attribute> attributes_;
|
||||
std::list<restriction> constraints_;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_OBJECT_HPP
|
||||
@@ -0,0 +1,429 @@
|
||||
#ifndef MATADOR_OBJECT_CACHE_HPP
|
||||
#define MATADOR_OBJECT_CACHE_HPP
|
||||
|
||||
#include "matador/object/object_proxy.hpp"
|
||||
#include "matador/object/object_resolver.hpp"
|
||||
|
||||
#include "matador/utils/identifier.hpp"
|
||||
#include "matador/utils/message_bus.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <typeindex>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
|
||||
namespace matador::object {
|
||||
struct cache_entry_base {
|
||||
virtual ~cache_entry_base() = default;
|
||||
|
||||
/**
|
||||
* @brief Gibt an, ob der Eintrag vollständig "tot" ist (weder Proxy noch Entity leben).
|
||||
*
|
||||
* Wird von @ref object_cache::sweep() verwendet, um Einträge ohne T-Kenntnis zu bereinigen.
|
||||
*/
|
||||
[[nodiscard]] virtual bool is_dead() const noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct cache_entry : cache_entry_base {
|
||||
std::weak_ptr<object_proxy<Type> > proxy;
|
||||
std::weak_ptr<Type> entity;
|
||||
|
||||
[[nodiscard]] bool is_dead() const noexcept override {
|
||||
return proxy.expired() && entity.expired();
|
||||
}
|
||||
};
|
||||
|
||||
struct object_cache_event {
|
||||
std::type_index type{typeid(void)};
|
||||
utils::identifier id{};
|
||||
std::chrono::steady_clock::time_point timestamp{};
|
||||
};
|
||||
|
||||
struct object_cache_accessed_event : object_cache_event {};
|
||||
struct object_cache_proxy_added_event : object_cache_event {};
|
||||
struct object_cache_entity_attached_event : object_cache_event {};
|
||||
struct object_cache_imported_event : object_cache_event {};
|
||||
struct object_cache_erased_event : object_cache_event {};
|
||||
|
||||
struct object_cache_sweep_event {
|
||||
std::size_t removed{};
|
||||
std::chrono::steady_clock::time_point timestamp{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Thread-sicherer Cache für Objekt-Proxies und (optional) geladene Entities.
|
||||
*
|
||||
* Der Cache verwaltet Einträge pro Kombination aus Objekt-Typ @p T und Primärschlüssel/Identifier.
|
||||
* Für jede (T, id)-Kombination können sowohl
|
||||
* - ein Proxy (@c object_proxy<T>) als auch
|
||||
* - die dazugehörige Entity (@c T)
|
||||
* zwischengespeichert werden.
|
||||
*
|
||||
* Der Cache hält dabei nur @c std::weak_ptr auf Proxy und Entity, d.h. er verlängert deren Lebensdauer nicht.
|
||||
*
|
||||
* @note Thread-Safety: Alle öffentlichen Methoden sind threadsafe (intern synchronisiert).
|
||||
* @note Semantik: Ein Eintrag kann aus dem Cache opportunistisch entfernt werden, sobald Proxy und Entity abgelaufen sind.
|
||||
*/
|
||||
class object_cache {
|
||||
public:
|
||||
/**
|
||||
* @brief Erzeugt einen leeren Cache.
|
||||
*/
|
||||
explicit object_cache(utils::message_bus &bus)
|
||||
: bus_(bus) {}
|
||||
|
||||
/**
|
||||
* @brief Liefert einen Proxy für (T, id) und erstellt ihn bei Bedarf.
|
||||
*
|
||||
* Falls bereits ein lebender Proxy im Cache existiert, wird er wiederverwendet.
|
||||
* Andernfalls wird ein neuer Proxy erzeugt, im Cache abgelegt und zurückgegeben.
|
||||
* Falls für (T, id) bereits eine Entity vorhanden ist, wird sie an den Proxy gebunden.
|
||||
*
|
||||
* @tparam Type Entity-Typ.
|
||||
* @tparam ResolverPointerType Zeiger-Typ auf einen Resolver, typischerweise
|
||||
* @c std::shared_ptr<object_resolver<T>> oder @c std::weak_ptr<object_resolver<T>>.
|
||||
* @param id Identifier/Primärschlüssel der Entity.
|
||||
* @param resolver_ptr Resolver (shared/weak), der zur Lazy-Auflösung durch den Proxy genutzt wird.
|
||||
* @return Shared-Pointer auf den (ggf. neu erstellten) Proxy.
|
||||
*
|
||||
* @note Diese Methode ist threadsafe.
|
||||
*/
|
||||
template<typename Type, typename ResolverPointerType>
|
||||
std::shared_ptr<object_proxy<Type>> acquire_proxy(utils::identifier id, ResolverPointerType &&resolver_ptr) {
|
||||
const auto k = make_key<Type>(id);
|
||||
|
||||
std::unique_lock lock(mutex_);
|
||||
|
||||
auto it = map_.find(k);
|
||||
if (it != map_.end()) {
|
||||
// found entry, return std::shared_ptr of proxy
|
||||
auto *entry = entry_cast_<Type>(it->second.get());
|
||||
if (auto proxy_ptr = entry->proxy.lock()) {
|
||||
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
return proxy_ptr;
|
||||
}
|
||||
|
||||
// if the proxy is dead, but the entity is alive, create a new proxy and attach entity
|
||||
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
|
||||
auto proxy_ptr = std::make_shared<object_proxy<Type>>(weak_resolver, id);
|
||||
|
||||
if (auto obj = entry->entity.lock()) {
|
||||
proxy_ptr->attach(std::move(obj));
|
||||
}
|
||||
|
||||
entry->proxy = proxy_ptr;
|
||||
|
||||
bus_.publish<object_cache_proxy_added_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
|
||||
// return the shared_ptr of the proxy
|
||||
return proxy_ptr;
|
||||
}
|
||||
|
||||
// entry couldn't be found, create a new entry
|
||||
auto entry_ptr = std::make_unique<cache_entry<Type>>();
|
||||
auto *entry = entry_ptr.get();
|
||||
|
||||
// create a weak resolver and shared proxy
|
||||
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
|
||||
auto proxy_ptr = std::make_shared<object_proxy<Type>>(weak_resolver, id);
|
||||
|
||||
// lock entity and attach to proxy
|
||||
if (auto obj = entry->entity.lock()) {
|
||||
proxy_ptr->attach(std::move(obj));
|
||||
}
|
||||
|
||||
// set the new proxy into cache entry
|
||||
entry->proxy = proxy_ptr;
|
||||
map_.emplace(k, std::move(entry_ptr));
|
||||
|
||||
bus_.publish<object_cache_proxy_added_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
|
||||
// return the shared_ptr of the proxy
|
||||
return proxy_ptr;
|
||||
}
|
||||
|
||||
template<typename Type, typename ResolverPointerType>
|
||||
bool import(const utils::identifier &id, const std::shared_ptr<object_proxy<Type>> &proxy, ResolverPointerType &&resolver_ptr) {
|
||||
if (!proxy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto obj = proxy->object();
|
||||
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
|
||||
const auto k = make_key<Type>(id);
|
||||
|
||||
std::unique_lock lock(mutex_);
|
||||
|
||||
auto it = map_.find(k);
|
||||
if (it != map_.end()) {
|
||||
auto *entry = entry_cast_<Type>(it->second.get());
|
||||
|
||||
if (auto existing_proxy = entry->proxy.lock()) {
|
||||
if (existing_proxy != proxy) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
proxy->primary_key(id);
|
||||
proxy->resolver(std::move(weak_resolver));
|
||||
|
||||
if (obj) {
|
||||
entry->entity = obj;
|
||||
proxy->attach(std::move(obj));
|
||||
}
|
||||
|
||||
entry->proxy = proxy;
|
||||
bus_.publish<object_cache_imported_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
return true;
|
||||
}
|
||||
|
||||
auto entry_ptr = std::make_unique<cache_entry<Type>>();
|
||||
auto *entry = entry_ptr.get();
|
||||
|
||||
proxy->primary_key(id);
|
||||
proxy->resolver(std::move(weak_resolver));
|
||||
|
||||
if (obj) {
|
||||
entry->entity = obj;
|
||||
proxy->attach(std::move(obj));
|
||||
}
|
||||
|
||||
entry->proxy = proxy;
|
||||
map_.emplace(k, std::move(entry_ptr));
|
||||
|
||||
bus_.publish<object_cache_imported_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @brief Verknüpft eine geladene Entity mit einem Cache-Eintrag (Type, id).
|
||||
*
|
||||
* Speichert die Entity als @c weak_ptr im Cache. Falls bereits ein Proxy existiert,
|
||||
* wird die Entity sofort an den Proxy gebunden.
|
||||
*
|
||||
* @tparam Type Entity-Typ.
|
||||
* @param id Identifier/Primärschlüssel der Entity.
|
||||
* @param obj Geladene Entity als @c shared_ptr.
|
||||
*
|
||||
* @note Diese Methode ist threadsafe.
|
||||
*/
|
||||
template<typename Type>
|
||||
void attach_entity(const utils::identifier &id, std::shared_ptr<Type> obj) {
|
||||
const auto k = make_key<Type>(id);
|
||||
|
||||
std::unique_lock lock(mutex_);
|
||||
|
||||
auto it = map_.find(k);
|
||||
if (it == map_.end()) {
|
||||
auto entry_ptr = std::make_unique<cache_entry<Type>>();
|
||||
auto *entry = entry_ptr.get();
|
||||
entry->entity = obj;
|
||||
map_.emplace(k, std::move(entry_ptr));
|
||||
bus_.publish<object_cache_entity_attached_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
return;
|
||||
}
|
||||
|
||||
auto *entry = entry_cast_<Type>(it->second.get());
|
||||
entry->entity = obj;
|
||||
|
||||
if (auto proxy = entry->proxy.lock()) {
|
||||
proxy->attach(std::move(obj));
|
||||
}
|
||||
|
||||
bus_.publish<object_cache_entity_attached_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
|
||||
prune_if_dead(it);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Liefert die aktuell geladene Entity für (Type, id), falls vorhanden.
|
||||
*
|
||||
* @tparam Type Entity-Typ.
|
||||
* @param id Identifier/Primärschlüssel der Entity.
|
||||
* @return @c shared_ptr auf die Entity oder ein leerer Pointer, falls nicht geladen/abgelaufen.
|
||||
*
|
||||
* @note Diese Methode ist threadsafe.
|
||||
*/
|
||||
template<typename Type>
|
||||
std::shared_ptr<Type> get_entity(const utils::identifier &id) {
|
||||
const auto k = make_key<Type>(id);
|
||||
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = map_.find(k);
|
||||
if (it == map_.end()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto *entry = entry_cast_<Type>(it->second.get());
|
||||
auto entity_ptr = entry->entity.lock();
|
||||
if (entity_ptr) {
|
||||
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
}
|
||||
prune_if_dead(it);
|
||||
return entity_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prüft, ob für (Type, id) aktuell eine lebende Entity verfügbar ist.
|
||||
*
|
||||
* @tparam Type Entity-Typ.
|
||||
* @param id Identifier/Primärschlüssel der Entity.
|
||||
* @return @c true, wenn die Entity-Referenz nicht abgelaufen ist; sonst @c false.
|
||||
*
|
||||
* @note Diese Methode ist threadsafe.
|
||||
*/
|
||||
template<typename Type>
|
||||
bool is_loaded(const utils::identifier &id) {
|
||||
const auto k = make_key<Type>(id);
|
||||
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = map_.find(k);
|
||||
if (it == map_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *entry = entry_cast_<Type>(it->second.get());
|
||||
const bool loaded = !entry->entity.expired();
|
||||
if (loaded) {
|
||||
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
}
|
||||
prune_if_dead(it);
|
||||
return loaded;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void erase(utils::identifier id) {
|
||||
const auto k = make_key<T>(id);
|
||||
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = map_.find(k);
|
||||
if (it == map_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto *e = entry_cast_<T>(it->second.get());
|
||||
if (auto p = e->proxy.lock()) {
|
||||
p->invalidate();
|
||||
}
|
||||
|
||||
map_.erase(it);
|
||||
bus_.publish<object_cache_erased_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Führt einen Cleanup-Lauf aus und entfernt Einträge, deren Proxy und Entity abgelaufen sind.
|
||||
*
|
||||
* @return Anzahl der entfernten Einträge.
|
||||
*
|
||||
* @note Diese Methode ist threadsafe.
|
||||
*/
|
||||
[[nodiscard]] std::size_t sweep() {
|
||||
std::unique_lock lock(mutex_);
|
||||
std::size_t removed = 0;
|
||||
|
||||
for (auto it = map_.begin(); it != map_.end(); ) {
|
||||
if (it->second->is_dead()) {
|
||||
it = map_.erase(it);
|
||||
++removed;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (removed > 0) {
|
||||
bus_.publish<object_cache_sweep_event>({removed, std::chrono::steady_clock::now()});
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
private:
|
||||
struct key {
|
||||
std::type_index type;
|
||||
utils::identifier id{};
|
||||
bool operator==(key const &other) const {
|
||||
return type == other.type && id == other.id;
|
||||
}
|
||||
};
|
||||
|
||||
struct key_hash {
|
||||
size_t operator()(key const &k) const noexcept {
|
||||
// (3) robustere Hash-Kombination als XOR
|
||||
size_t seed = std::hash<std::type_index>()(k.type);
|
||||
const size_t h2 = std::hash<utils::identifier>()(k.id);
|
||||
seed ^= h2 + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static key make_key(const utils::identifier &id) {
|
||||
return key{std::type_index(typeid(T)), id};
|
||||
}
|
||||
|
||||
// (4) Debug-Absicherung für type-erased Casts
|
||||
template<typename T>
|
||||
static cache_entry<T> *entry_cast_(cache_entry_base *base) {
|
||||
#ifndef NDEBUG
|
||||
auto *p = dynamic_cast<cache_entry<T> *>(base);
|
||||
assert(p && "object_cache: Type mismatch in cache entry (key/type invariant violated)");
|
||||
return p;
|
||||
#else
|
||||
return static_cast<cache_entry<T> *>(base);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
using enable_if_resolver_shared_ptr_t = std::enable_if_t<std::is_base_of_v<object_resolver<T>, U>, int>;
|
||||
|
||||
template<typename T, typename U>
|
||||
using enable_if_resolver_weak_ptr_t = std::enable_if_t<std::is_base_of_v<object_resolver<T>, U>, int>;
|
||||
|
||||
// shared_ptr<Derived> -> weak_ptr<Base>
|
||||
template<typename T, typename U, enable_if_resolver_shared_ptr_t<T, U> = 0>
|
||||
static std::weak_ptr<object_resolver<T>> to_weak(const std::shared_ptr<U> &s) {
|
||||
return std::weak_ptr<object_resolver<T>>(std::static_pointer_cast<object_resolver<T>>(s));
|
||||
}
|
||||
|
||||
// weak_ptr<Derived> -> weak_ptr<Base>
|
||||
template<typename T, typename U, enable_if_resolver_weak_ptr_t<T, U> = 0>
|
||||
static std::weak_ptr<object_resolver<T>> to_weak(const std::weak_ptr<U> &w) {
|
||||
return std::weak_ptr<object_resolver<T>>(w);
|
||||
}
|
||||
|
||||
// (2) Opportunistischer Cleanup: entferne Entry, wenn beide weak_ptr abgelaufen sind.
|
||||
template<typename IteratorType>
|
||||
bool prune_if_dead(IteratorType &it) {
|
||||
if (it == map_.end()) {
|
||||
return false;
|
||||
}
|
||||
if (it->second && it->second->is_dead()) {
|
||||
map_.erase(it);
|
||||
it = map_.end();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void prune_if_dead_typed(typename std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash>::iterator &it) {
|
||||
(void)Type{}; // T bleibt im Interface, damit bestehende Call-Sites unverändert bleiben können.
|
||||
prune_if_dead(it);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::shared_mutex mutex_{};
|
||||
utils::message_bus &bus_;
|
||||
std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash> map_;
|
||||
};
|
||||
|
||||
// --- Inline Anpassungen: typed prune nach Zugriff (damit wir T haben) ---
|
||||
|
||||
// ... existing code ...
|
||||
// (Die typed prune wird oben benutzt; wir rufen sie direkt im Codepfad anstelle von prune_if_dead_)
|
||||
|
||||
} // namespace matador::object
|
||||
#endif //MATADOR_OBJECT_CACHE_HPP
|
||||
@@ -1,70 +0,0 @@
|
||||
#ifndef QUERY_TABLE_DEFINITION_HPP
|
||||
#define QUERY_TABLE_DEFINITION_HPP
|
||||
|
||||
#include "matador/object/attribute_definition.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
class object_definition final {
|
||||
private:
|
||||
using column_by_index = std::vector<attribute_definition>;
|
||||
using column_index_pair = std::pair<std::reference_wrapper<attribute_definition>, column_by_index::difference_type>;
|
||||
using column_by_name_map = std::unordered_map<std::string, column_index_pair>;
|
||||
|
||||
public:
|
||||
using iterator = column_by_index::iterator;
|
||||
using const_iterator = column_by_index::const_iterator;
|
||||
|
||||
object_definition() = default;
|
||||
object_definition(std::initializer_list<attribute_definition> columns);
|
||||
explicit object_definition(const std::vector<attribute_definition> &columns);
|
||||
object_definition(const object_definition &x);
|
||||
object_definition& operator=(const object_definition &x);
|
||||
object_definition(object_definition&&) noexcept = default;
|
||||
object_definition& operator=(object_definition&&) noexcept = default;
|
||||
~object_definition() = default;
|
||||
|
||||
[[nodiscard]] bool has_primary_key() const;
|
||||
[[nodiscard]] std::optional<attribute_definition> primary_key() const;
|
||||
|
||||
template < typename Type >
|
||||
void append(const std::string &name, long size = -1) {
|
||||
append(make_column<Type>(name, size));
|
||||
}
|
||||
void append(attribute_definition col);
|
||||
|
||||
[[nodiscard]] const std::vector<attribute_definition>& columns() const;
|
||||
|
||||
[[nodiscard]] const attribute_definition& at(const std::string &name) const;
|
||||
[[nodiscard]] const attribute_definition& at(size_t index) const;
|
||||
|
||||
iterator find(const std::string &column_name);
|
||||
[[nodiscard]] const_iterator find(const std::string &column_name) const;
|
||||
|
||||
iterator begin();
|
||||
[[nodiscard]] const_iterator begin() const;
|
||||
[[nodiscard]] const_iterator cbegin() const;
|
||||
|
||||
iterator end();
|
||||
[[nodiscard]] const_iterator end() const;
|
||||
[[nodiscard]] const_iterator cend() const;
|
||||
|
||||
[[nodiscard]] size_t size() const;
|
||||
[[nodiscard]] bool empty() const;
|
||||
void clear();
|
||||
|
||||
private:
|
||||
void init();
|
||||
void add_to_map(attribute_definition &col, size_t index);
|
||||
|
||||
private:
|
||||
column_by_index columns_;
|
||||
column_by_name_map columns_by_name_;
|
||||
|
||||
int pk_index_{-1};
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_TABLE_DEFINITION_HPP
|
||||
@@ -0,0 +1,198 @@
|
||||
#ifndef MATADOR_OBJECT_GENERATOR_HPP
|
||||
#define MATADOR_OBJECT_GENERATOR_HPP
|
||||
|
||||
#include "matador/object/basic_repository.hpp"
|
||||
#include "matador/object/object.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/primary_key_attribute.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::object {
|
||||
class pk_type_determinator {
|
||||
private:
|
||||
pk_type_determinator() = default;
|
||||
|
||||
public:
|
||||
template<class Type>
|
||||
static utils::basic_type determine() {
|
||||
pk_type_determinator determinator;
|
||||
Type t;
|
||||
|
||||
access::process(determinator, t);
|
||||
return determinator.type_;
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void on_primary_key(const char *, ValueType &/*pk*/, const utils::primary_key_attribute& attr) {
|
||||
type_ = utils::data_type_traits<ValueType>::type(attr.size());
|
||||
}
|
||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||
template < class Type >
|
||||
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/) {}
|
||||
static void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/) {}
|
||||
template<class Pointer>
|
||||
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class Pointer>
|
||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
utils::basic_type type_{};
|
||||
};
|
||||
|
||||
class object_generator {
|
||||
private:
|
||||
explicit object_generator(basic_repository& repo, const std::shared_ptr<object> &object);
|
||||
|
||||
public:
|
||||
template < class Type >
|
||||
static std::shared_ptr<object> generate(basic_repository &repo, const std::string &name) {
|
||||
return generate(std::make_unique<Type>(), repo, name);
|
||||
}
|
||||
|
||||
template < class Type >
|
||||
static std::shared_ptr<object> generate(std::unique_ptr<Type>&& t,
|
||||
basic_repository &repo,
|
||||
const std::string &name,
|
||||
const std::string &join_column = "",
|
||||
const std::string &inverse_join_column = "") {
|
||||
const std::type_index ti(typeid(Type));
|
||||
if (repo.has_object_for_type(ti)) {
|
||||
auto obj = repo.object_for_type(ti);
|
||||
repo.remove_object_for_type(ti);
|
||||
obj->update_name(name);
|
||||
return obj;
|
||||
}
|
||||
|
||||
auto obj = std::make_shared<object>(name);
|
||||
std::ignore = repo.provide_object_in_advance(ti, obj);
|
||||
object_generator gen(repo, obj);
|
||||
access::process(gen, *t);
|
||||
if (!join_column.empty() && !inverse_join_column.empty()) {
|
||||
gen.prepare_relation_table(join_column, inverse_join_column);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<typename BaseType>
|
||||
static void on_base(const BaseType&) {}
|
||||
template < class Type >
|
||||
void on_primary_key(const char *, Type &x, const utils::primary_key_attribute& attr);
|
||||
void on_revision(const char *id, uint64_t &rev);
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr);
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr);
|
||||
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) {
|
||||
on_foreign_key(id, x);
|
||||
create_fk_constraint<typename Pointer::value_type>(id);
|
||||
}
|
||||
template<class Pointer>
|
||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
template <class Pointer>
|
||||
void on_foreign_key(const char *id, Pointer &/*x*/) {
|
||||
const auto type = pk_type_determinator::determine<typename Pointer::value_type>();
|
||||
auto &ref = object_->attributes_.emplace_back(id, type, utils::constraints::ForeignKey, null_option_type::NotNull);
|
||||
ref.index_ = object_->attributes_.size() - 1;
|
||||
ref.owner_ = object_;
|
||||
}
|
||||
template<class ContainerType>
|
||||
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
template<typename ValueType>
|
||||
attribute &emplace_attribute(const char *id, const utils::field_attributes& attr, null_option_type null_option) {
|
||||
auto &ref = object_->attributes_.emplace_back(id, utils::data_type_traits<ValueType>::type(attr.size()), attr, null_option);
|
||||
ref.index_ = object_->attributes_.size() - 1;
|
||||
ref.owner_ = object_;
|
||||
return ref;
|
||||
}
|
||||
|
||||
void create_pk_constraint(const std::string& name) const;
|
||||
template<typename Type>
|
||||
void create_fk_constraint(const std::string& name) const;
|
||||
void create_unique_constraint(const std::string& name) const;
|
||||
|
||||
[[nodiscard]] std::vector<attribute>::iterator find_attribute_by_name(const std::string &name) const;
|
||||
|
||||
void prepare_primary_key(const attribute &ref, utils::identifier &&pk) const;
|
||||
void prepare_relation_table(const std::string &join_column, const std::string &inverse_join_column) const;
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] std::shared_ptr<object> fk_object() const;
|
||||
|
||||
static std::shared_ptr<object> acquire_object(basic_repository &repo, const std::type_index &ti, const std::string& name);
|
||||
private:
|
||||
basic_repository &repo_;
|
||||
std::shared_ptr<object> object_;
|
||||
};
|
||||
|
||||
template<typename ValueType>
|
||||
void object_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) {
|
||||
auto cs = utils::constraints::PrimaryKey;
|
||||
if (attr.generator() == utils::generator_type::Identity) {
|
||||
cs |= utils::constraints::Identity;
|
||||
}
|
||||
auto &ref = emplace_attribute<ValueType>(id, { attr.size(), cs }, null_option_type::NotNull);
|
||||
prepare_primary_key(ref, utils::identifier(x));
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void object_generator::on_attribute(const char *id, Type &/*x*/, const utils::field_attributes &attr) {
|
||||
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::NotNull);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void object_generator::on_attribute(const char *id, std::optional<Type> & /*x*/, const utils::field_attributes &attr) {
|
||||
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::Nullable);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void object_generator::create_fk_constraint(const std::string& name) const {
|
||||
const auto pk_attr = find_attribute_by_name(name);
|
||||
if (pk_attr == std::end(object_->attributes_)) {
|
||||
return;
|
||||
}
|
||||
const auto obj = fk_object<Type>();
|
||||
restriction pk_constraint(pk_attr->index());
|
||||
pk_constraint.options_ |= utils::constraints::ForeignKey;
|
||||
pk_constraint.owner_ = object_;
|
||||
pk_constraint.reference_ = obj;
|
||||
object_->constraints_.emplace_back(std::move(pk_constraint));
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
std::shared_ptr<object> object_generator::fk_object() const {
|
||||
const auto ti = std::type_index(typeid(Type));
|
||||
if (const auto result = repo_.basic_info(ti)) {
|
||||
return result->get().object();
|
||||
}
|
||||
|
||||
if (repo_.has_object_for_type(ti)) {
|
||||
return repo_.object_for_type(ti);
|
||||
}
|
||||
const auto obj = repo_.provide_object_in_advance(ti, std::make_shared<object>(""));
|
||||
object_generator gen(repo_, obj);
|
||||
Type t;
|
||||
access::process(gen, t);
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
||||
#endif //MATADOR_OBJECT_GENERATOR_HPP
|
||||
@@ -2,37 +2,64 @@
|
||||
#define OBJECT_INFO_HPP
|
||||
|
||||
#include "matador/object/basic_object_info.hpp"
|
||||
#include "matador/object/object_definition.hpp"
|
||||
#include "matador/object/observer.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace matador::object {
|
||||
class schema_node;
|
||||
class repository_node;
|
||||
|
||||
template<typename Type>
|
||||
class object_info final : public basic_object_info {
|
||||
public:
|
||||
explicit object_info(const std::shared_ptr<schema_node>& node,
|
||||
std::shared_ptr<attribute_definition> &&ref_column)
|
||||
: basic_object_info(node, typeid(Type), {}, std::move(ref_column), {}) {
|
||||
}
|
||||
explicit object_info(const std::shared_ptr<schema_node>& node,
|
||||
utils::identifier &&pk,
|
||||
std::shared_ptr<attribute_definition> &&ref_column,
|
||||
object_definition &&definition)
|
||||
: basic_object_info(node, typeid(Type), std::move(pk), std::move(ref_column), std::move(definition)) {
|
||||
using create_func = std::function<std::unique_ptr<Type>()>;
|
||||
|
||||
object_info(const repository_node& node,
|
||||
const std::shared_ptr<class object> &obj,
|
||||
std::vector<std::unique_ptr<observer<Type>>>&& observers,
|
||||
create_func&& creator)
|
||||
: basic_object_info(node, obj)
|
||||
, creator_(std::move(creator))
|
||||
, observers_(std::move(observers)){}
|
||||
|
||||
explicit object_info(const repository_node& node)
|
||||
: basic_object_info(node, {}) {
|
||||
}
|
||||
|
||||
const Type &prototype() const { return prototype_; }
|
||||
std::unique_ptr<Type> create() const { return creator_(); }
|
||||
|
||||
void on_attach() const override {
|
||||
for (auto &observer : observers_) {
|
||||
observer->on_attach(node_, prototype_);
|
||||
}
|
||||
}
|
||||
|
||||
void on_detach() const override {
|
||||
for (auto &observer : observers_) {
|
||||
observer->on_detach(node_, prototype_);
|
||||
}
|
||||
}
|
||||
|
||||
void register_observer(std::unique_ptr<observer<Type>>&& observer) {
|
||||
observers_.push_back(std::move(observer));
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<observer<Type>>>& observers() const {
|
||||
return observers_;
|
||||
}
|
||||
|
||||
private:
|
||||
Type prototype_;
|
||||
create_func creator_{[]{ return std::make_unique<Type>(); }};
|
||||
std::vector<std::unique_ptr<observer<Type>>> observers_;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
using object_info_ref = std::reference_wrapper<const object_info<Type>>;
|
||||
|
||||
namespace detail {
|
||||
struct null_type {
|
||||
};
|
||||
struct null_type {};
|
||||
}
|
||||
|
||||
using null_info = object_info<detail::null_type>;
|
||||
|
||||
@@ -1,42 +1,190 @@
|
||||
#ifndef OBJECT_PROXY_HPP
|
||||
#define OBJECT_PROXY_HPP
|
||||
|
||||
#include "matador/object/primary_key_resolver.hpp"
|
||||
|
||||
#include "matador/object/object_resolver.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <mutex>
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
class basic_object_proxy {
|
||||
public:
|
||||
virtual ~basic_object_proxy() = default;
|
||||
|
||||
[[nodiscard]] virtual void *get() const = 0;
|
||||
enum class object_state : uint8_t {
|
||||
Transient,
|
||||
Persistent,
|
||||
Detached,
|
||||
Removed
|
||||
};
|
||||
|
||||
template<class Type>
|
||||
class object_proxy : public basic_object_proxy {
|
||||
class object_proxy final {
|
||||
public:
|
||||
object_proxy() = default;
|
||||
explicit object_proxy(Type* obj)
|
||||
: obj_(obj) {}
|
||||
explicit object_proxy(std::unique_ptr<Type> obj)
|
||||
: obj_(std::move(obj)) {}
|
||||
|
||||
[[nodiscard]] void *get() const override { return static_cast<void*>(obj_.get()); }
|
||||
// Lazy
|
||||
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, utils::identifier id)
|
||||
: resolver_(std::move(resolver))
|
||||
, pk_(std::move(id))
|
||||
, state_(object_state::Persistent) {
|
||||
}
|
||||
|
||||
Type* operator->() const { return obj_.get(); }
|
||||
Type& operator*() { return *obj_; }
|
||||
const Type& operator*() const { return *obj_; }
|
||||
// Eager
|
||||
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
|
||||
: obj_(std::move(obj))
|
||||
, resolver_(std::move(resolver))
|
||||
, pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{})
|
||||
, state_(obj_ ? object_state::Persistent : object_state::Detached) {
|
||||
}
|
||||
|
||||
Type* pointer() const { return obj_.get(); }
|
||||
// Transient
|
||||
explicit object_proxy(std::shared_ptr<Type> obj)
|
||||
: obj_(std::move(obj))
|
||||
, pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{}) {
|
||||
}
|
||||
|
||||
Type& ref() { return *obj_; }
|
||||
const Type& ref() const { return *obj_; }
|
||||
void attach(std::shared_ptr<Type> obj) {
|
||||
std::lock_guard lock(mutex_);
|
||||
|
||||
void reset(Type* obj) { obj_.reset(obj); }
|
||||
obj_ = std::move(obj);
|
||||
if (!obj_) {
|
||||
pk_.clear();
|
||||
state_ = object_state::Detached;
|
||||
return;
|
||||
}
|
||||
|
||||
pk_ = primary_key_resolver::resolve_object(*obj_).pk;
|
||||
state_ = object_state::Persistent;
|
||||
}
|
||||
|
||||
void resolver(std::weak_ptr<object_resolver<Type>> resolver) {
|
||||
std::lock_guard lock(mutex_);
|
||||
resolver_ = std::move(resolver);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Type> object() const {
|
||||
return resolve_object();
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
std::lock_guard lock(mutex_);
|
||||
obj_.reset();
|
||||
resolver_.reset();
|
||||
state_ = object_state::Detached;
|
||||
}
|
||||
|
||||
[[nodiscard]] void *raw_pointer() const { return static_cast<void *>(pointer()); }
|
||||
|
||||
Type *operator->() {
|
||||
auto *ptr = pointer();
|
||||
if (!ptr) {
|
||||
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const Type *operator->() const {
|
||||
auto *ptr = pointer();
|
||||
if (!ptr) {
|
||||
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
Type &operator*() {
|
||||
auto *ptr = pointer();
|
||||
if (!ptr) {
|
||||
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||
}
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
const Type &operator*() const {
|
||||
auto *ptr = pointer();
|
||||
if (!ptr) {
|
||||
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||
}
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
Type *pointer() const {
|
||||
return resolve_object().get();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
std::lock_guard lock(mutex_);
|
||||
return !obj_ && resolver_.expired();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool valid() const { return !empty(); }
|
||||
|
||||
[[nodiscard]] bool has_primary_key() const {
|
||||
std::lock_guard lock(mutex_);
|
||||
return !pk_.is_null();
|
||||
}
|
||||
|
||||
[[nodiscard]] utils::identifier primary_key() const {
|
||||
std::lock_guard lock(mutex_);
|
||||
return pk_;
|
||||
}
|
||||
|
||||
void primary_key(const utils::identifier &pk) {
|
||||
std::lock_guard lock(mutex_);
|
||||
pk_ = pk;
|
||||
}
|
||||
|
||||
bool is_persistent() const { return is_state(object_state::Persistent); }
|
||||
bool is_transient() const { return is_state(object_state::Transient); }
|
||||
bool is_detached() const { return is_state(object_state::Detached); }
|
||||
bool is_removed() const { return is_state(object_state::Removed); }
|
||||
|
||||
bool is_state(const object_state state) const {
|
||||
std::lock_guard lock(mutex_);
|
||||
return state_ == state;
|
||||
}
|
||||
|
||||
void change_state(const object_state state) {
|
||||
std::lock_guard lock(mutex_);
|
||||
state_ = state;
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<Type> obj_{};
|
||||
};
|
||||
std::shared_ptr<Type> resolve_object() const {
|
||||
std::shared_ptr<Type> current;
|
||||
std::shared_ptr<object_resolver<Type>> resolver;
|
||||
utils::identifier pk;
|
||||
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
if (obj_) {
|
||||
return obj_;
|
||||
}
|
||||
|
||||
resolver = resolver_.lock();
|
||||
if (!resolver) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pk = pk_;
|
||||
}
|
||||
|
||||
current = resolver->resolve(pk);
|
||||
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
if (!obj_) {
|
||||
obj_ = std::move(current);
|
||||
}
|
||||
return obj_;
|
||||
}
|
||||
}
|
||||
private:
|
||||
mutable std::shared_ptr<Type> obj_{};
|
||||
mutable std::weak_ptr<object_resolver<Type>> resolver_{};
|
||||
utils::identifier pk_{};
|
||||
object_state state_{object_state::Transient};
|
||||
mutable std::mutex mutex_{};
|
||||
};
|
||||
}
|
||||
#endif //OBJECT_PROXY_HPP
|
||||
|
||||
@@ -4,47 +4,179 @@
|
||||
#include "matador/utils/identifier.hpp"
|
||||
|
||||
#include "matador/object/object_proxy.hpp"
|
||||
#include "matador/object/primary_key_resolver.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::object {
|
||||
struct null_object_ptr_t {
|
||||
constexpr null_object_ptr_t() = default;
|
||||
};
|
||||
|
||||
inline constexpr null_object_ptr_t nullobj{};
|
||||
|
||||
template <typename Type>
|
||||
class object_ptr {
|
||||
public:
|
||||
object_ptr()
|
||||
: ptr_(std::make_shared<object_proxy<Type>>()) {}
|
||||
explicit object_ptr(Type *obj)
|
||||
: ptr_(std::make_shared<object_proxy<Type>>(obj))
|
||||
, pk_(primary_key_resolver::resolve_object(*obj).pk) {}
|
||||
object_ptr(const object_ptr &other) : ptr_(other.ptr_) {}
|
||||
object_ptr(object_ptr &&other) noexcept : ptr_(std::move(other.ptr_)) {}
|
||||
object_ptr &operator=(const object_ptr &other) = default;
|
||||
object_ptr &operator=(object_ptr &&other) = default;
|
||||
object_ptr()
|
||||
: proxy_(std::make_shared<object_proxy<Type>>()) {}
|
||||
|
||||
using value_type = Type;
|
||||
Type* operator->() const { return ptr_->pointer(); }
|
||||
Type& operator*() { return ptr_->ref(); }
|
||||
const Type& operator*() const { return ptr_->ref(); }
|
||||
object_ptr(null_object_ptr_t) {}
|
||||
|
||||
[[nodiscard]] bool empty() const { return ptr_->pointer() == nullptr; }
|
||||
void reset(Type *obj) {
|
||||
ptr_->reset(obj);
|
||||
pk_ = primary_key_resolver::resolve_object(*obj).pk;
|
||||
explicit object_ptr(std::shared_ptr<Type> obj)
|
||||
: proxy_(std::make_shared<object_proxy<Type>>(std::move(obj))) {}
|
||||
|
||||
explicit object_ptr(std::shared_ptr<object_proxy<Type>> obj)
|
||||
: proxy_(std::move(obj)) {}
|
||||
|
||||
object_ptr(const object_ptr &other) = default;
|
||||
object_ptr(object_ptr &&other) noexcept = default;
|
||||
object_ptr& operator=(const object_ptr &other) = default;
|
||||
object_ptr& operator=(object_ptr &&other) noexcept = default;
|
||||
|
||||
object_ptr& operator=(null_object_ptr_t) {
|
||||
proxy_.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Type* get() const { return static_cast<Type*>(ptr_->pointer()); }
|
||||
bool operator==(const object_ptr &other) const {
|
||||
if (proxy_ == other.proxy_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
operator bool() { return valid(); }
|
||||
bool valid() { return ptr_ != nullptr; }
|
||||
if (!proxy_ || !other.proxy_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (has_primary_key() && other.has_primary_key()) {
|
||||
return primary_key() == other.primary_key();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(null_object_ptr_t) const {
|
||||
return empty();
|
||||
}
|
||||
|
||||
bool operator!=(const object_ptr &other) const { return !operator==(other); }
|
||||
bool operator!=(null_object_ptr_t) const { return !empty(); }
|
||||
|
||||
using value_type = Type;
|
||||
|
||||
Type *operator->() const {
|
||||
return checked_get();
|
||||
}
|
||||
|
||||
Type &operator*() {
|
||||
return *checked_get();
|
||||
}
|
||||
|
||||
const Type &operator*() const {
|
||||
return *checked_get();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return proxy_ == nullptr || proxy_->empty();
|
||||
}
|
||||
|
||||
Type *get() const {
|
||||
return proxy_ ? proxy_->pointer() : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Type> object() const {
|
||||
return proxy_ ? proxy_->object() : nullptr;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
proxy_.reset();
|
||||
}
|
||||
|
||||
void reset(std::shared_ptr<object_proxy<Type>> proxy) {
|
||||
proxy_ = std::move(proxy);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<object_proxy<Type>> proxy() const {
|
||||
return proxy_;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return proxy_ != nullptr && !proxy_->empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool has_primary_key() const {
|
||||
return proxy_ != nullptr && proxy_->has_primary_key();
|
||||
}
|
||||
|
||||
[[nodiscard]] utils::identifier primary_key() const {
|
||||
return proxy_ ? proxy_->primary_key() : utils::identifier{};
|
||||
}
|
||||
|
||||
void primary_key(const utils::identifier &pk) {
|
||||
ensure_proxy();
|
||||
proxy_->primary_key(pk);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_persistent() const {
|
||||
return proxy_ != nullptr && proxy_->is_persistent();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_transient() const {
|
||||
return proxy_ != nullptr && proxy_->is_transient();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_detached() const {
|
||||
return proxy_ != nullptr && proxy_->is_detached();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_removed() const {
|
||||
return proxy_ != nullptr && proxy_->is_removed();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_state(const object_state state) const {
|
||||
return proxy_ != nullptr && proxy_->is_state(state);
|
||||
}
|
||||
|
||||
void change_state(object_state s) const {
|
||||
if (proxy_) {
|
||||
proxy_->change_state(s);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const utils::identifier& primary_key() const { return pk_; }
|
||||
void primary_key(const utils::identifier &pk) { pk_ = pk; }
|
||||
private:
|
||||
std::shared_ptr<object_proxy<Type>> ptr_{};
|
||||
utils::identifier pk_;
|
||||
Type *checked_get() const {
|
||||
auto *ptr = get();
|
||||
if (!ptr) {
|
||||
throw std::runtime_error("Cannot dereference empty object_ptr");
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void ensure_proxy() {
|
||||
if (!proxy_) {
|
||||
proxy_ = std::make_shared<object_proxy<Type>>();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<object_proxy<Type>> proxy_{};
|
||||
};
|
||||
|
||||
template<typename>
|
||||
struct is_object_ptr : std::false_type {
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct is_object_ptr<object_ptr<Type> > : std::true_type {
|
||||
};
|
||||
|
||||
template<class Type, typename... Args>
|
||||
object_ptr<Type> make_object(Args &&... args) {
|
||||
return object_ptr<Type>(std::make_shared<Type>(std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
|
||||
#endif //OBJECT_PTR_HPP
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef MATADOR_OBJECT_LOADER_HPP
|
||||
#define MATADOR_OBJECT_LOADER_HPP
|
||||
|
||||
#include "matador/object/abstract_type_resolver.hpp"
|
||||
#include "matador/object/abstract_joined_resolver.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::utils {
|
||||
class identifier;
|
||||
}
|
||||
|
||||
namespace matador::object {
|
||||
template<typename Type>
|
||||
class object_resolver : public abstract_type_resolver {
|
||||
public:
|
||||
object_resolver() : abstract_type_resolver(typeid(Type)) {}
|
||||
|
||||
virtual std::shared_ptr<Type> resolve(const utils::identifier& id) = 0;
|
||||
};
|
||||
template<typename Type>
|
||||
class joined_object_resolver : public abstract_joined_resolver, public object_resolver<Type> {
|
||||
public:
|
||||
joined_object_resolver(const std::type_index& root_type, const std::string& join_column)
|
||||
: abstract_joined_resolver(root_type, typeid(Type), join_column) {}
|
||||
|
||||
std::shared_ptr<Type> resolve(const utils::identifier& id) override = 0;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_OBJECT_LOADER_HPP
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef MATADOR_OBJECT_RESOLVER_FACTORY_HPP
|
||||
#define MATADOR_OBJECT_RESOLVER_FACTORY_HPP
|
||||
|
||||
#include "matador/object/abstract_type_resolver_factory.hpp"
|
||||
#include "matador/object/object_resolver.hpp"
|
||||
|
||||
namespace matador::object {
|
||||
class object_resolver_factory : public abstract_type_resolver_factory {
|
||||
public:
|
||||
template<class Type>
|
||||
[[nodiscard]] std::shared_ptr<object_resolver<Type>> resolver() const {
|
||||
const auto res = acquire_object_resolver(std::type_index(typeid(Type)));
|
||||
if (!res) {
|
||||
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
|
||||
}
|
||||
|
||||
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_OBJECT_RESOLVER_FACTORY_HPP
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user