initial commit
This commit is contained in:
commit
4ff7f0eee9
5
.gitignore
vendored
Executable file
5
.gitignore
vendored
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
build
|
||||||
|
.cache
|
||||||
|
subprojects/*
|
||||||
|
!subprojects/hack.wrap
|
||||||
|
!subprojects/nlohmann_json.wrap
|
6
README.md
Executable file
6
README.md
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
# Билиотека для работы с PostgreSQL
|
||||||
|
|
||||||
|
Данный продукт имеет сугубо узкое направление деятельности и предназначен для обслуживания
|
||||||
|
подключений к БД из серера
|
||||||
|
|
||||||
|
Пример реализации расположен в папке test.
|
51
meson.build
Executable file
51
meson.build
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
project(
|
||||||
|
meson.current_source_dir().split('/').get(-1),
|
||||||
|
'cpp',
|
||||||
|
version : '1.0.0',
|
||||||
|
default_options : [
|
||||||
|
'warning_level=1',
|
||||||
|
'optimization=3',
|
||||||
|
'cpp_std=c++20',
|
||||||
|
])
|
||||||
|
|
||||||
|
add_project_arguments (
|
||||||
|
'-Wpedantic',
|
||||||
|
'-Wno-shadow',
|
||||||
|
'-Wno-unused-but-set-variable',
|
||||||
|
'-Wno-comment',
|
||||||
|
'-Wno-unused-parameter',
|
||||||
|
'-Wno-unused-value',
|
||||||
|
'-Wno-unused-header',
|
||||||
|
'-Wno-missing-field-initializers',
|
||||||
|
'-Wno-narrowing',
|
||||||
|
'-Wno-deprecated-enum-enum-conversion',
|
||||||
|
'-Wno-volatile',
|
||||||
|
'-Wno-format-security',
|
||||||
|
'-Wno-switch',
|
||||||
|
'-Wno-ignored-attributes',
|
||||||
|
'-Wno-unused-variable',
|
||||||
|
'-Wno-deprecated-enum-enum-conversion',
|
||||||
|
language: 'cpp'
|
||||||
|
)
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
|
||||||
|
#args = ['-lglfw', '-ldl', '-lGL', '-lpthread', '-lX11', '-lXxf86vm', '-lXrandr', '-lXi']
|
||||||
|
args = []
|
||||||
|
deps = []
|
||||||
|
inc = []
|
||||||
|
|
||||||
|
deps = [
|
||||||
|
dependency('uuid'),
|
||||||
|
dependency('threads'),
|
||||||
|
dependency('libpqxx'),
|
||||||
|
subproject('hack').get_variable('hack_dep'),
|
||||||
|
subproject('nlohmann_json').get_variable('nlohmann_json_dep'),
|
||||||
|
]
|
||||||
|
|
||||||
|
subdir('src')
|
||||||
|
subdir('tests')
|
||||||
|
|
||||||
|
message('==================================================================================================================')
|
||||||
|
message('=== т.к. мы не включаем зависимости в проект, котрый пушм на гит, то их нужно все подключить в deps (см. выше) ===')
|
||||||
|
message('==================================================================================================================')
|
22
run.sh
Executable file
22
run.sh
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/zsh
|
||||||
|
|
||||||
|
PROJECT_NAME=$(basename $PWD)
|
||||||
|
|
||||||
|
run() {
|
||||||
|
command meson compile -C build
|
||||||
|
if [[ -z "$1" ]]; then
|
||||||
|
cd build
|
||||||
|
./tests/$PROJECT_NAME
|
||||||
|
cd ..
|
||||||
|
else
|
||||||
|
meson test $1 -C build
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d "build" ]; then
|
||||||
|
run
|
||||||
|
else
|
||||||
|
command meson setup build
|
||||||
|
run
|
||||||
|
fi
|
||||||
|
|
26
src/meson.build
Executable file
26
src/meson.build
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
inc += include_directories('.')
|
||||||
|
headers = [
|
||||||
|
'pgxx/pgxx.hpp',
|
||||||
|
'pgxx/query_builder.hpp',
|
||||||
|
|
||||||
|
'pgxx/utils/define.hpp',
|
||||||
|
'pgxx/utils/using.hpp',
|
||||||
|
'pgxx/utils/var.hpp',
|
||||||
|
]
|
||||||
|
|
||||||
|
sources = [ ]
|
||||||
|
|
||||||
|
lib = library(
|
||||||
|
meson.project_name(),
|
||||||
|
include_directories : inc,
|
||||||
|
sources: [headers, sources],
|
||||||
|
dependencies : deps,
|
||||||
|
cpp_args: args
|
||||||
|
)
|
||||||
|
|
||||||
|
pgxx_dep = declare_dependency(
|
||||||
|
include_directories: inc,
|
||||||
|
link_with: lib,
|
||||||
|
)
|
||||||
|
|
||||||
|
deps += pgxx_dep
|
115
src/pgxx/pgxx.hpp
Normal file
115
src/pgxx/pgxx.hpp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "hack/utils/singleton.hpp"
|
||||||
|
#include "hack/exception/exception.hpp"
|
||||||
|
#include "hack/logger/logger.hpp"
|
||||||
|
|
||||||
|
#include "pgxx/utils/define.hpp"
|
||||||
|
#include "pgxx/utils/using.hpp"
|
||||||
|
#include "pgxx/utils/var.hpp"
|
||||||
|
#include "pgxx/query_builder.hpp"
|
||||||
|
#include "pgxx/pool_connection.hpp"
|
||||||
|
|
||||||
|
namespace pgxx
|
||||||
|
{
|
||||||
|
class manager : public hack::utils::singleton<manager>
|
||||||
|
{
|
||||||
|
friend hack::utils::singleton<manager>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~manager() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
manager() = default;
|
||||||
|
std::map<std::string, pool_connection> m_data_connections;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void init(std::string connection_name, int connection_count, std::string connection_url)
|
||||||
|
{
|
||||||
|
m_data_connections[connection_name] = pool_connection { connection_count, connection_url };
|
||||||
|
hack::log("")("make connection [", connection_name, "] completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
std::string prepare(const std::string func_name, const Args&... args)
|
||||||
|
{
|
||||||
|
std::string query;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
query = builder::make_query(func_name, args...);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
hack::exception ex;
|
||||||
|
ex.description("database dont create query from args");
|
||||||
|
ex.system_error(e);
|
||||||
|
ex.params("query", query);
|
||||||
|
ex.variadic_params(args...);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON execute(const std::string connection_name, std::string query)
|
||||||
|
{
|
||||||
|
JSON result;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto c = m_data_connections[connection_name].get();
|
||||||
|
|
||||||
|
pqxx::result r;
|
||||||
|
pqxx::work work { *c };
|
||||||
|
r = work.exec(query);
|
||||||
|
|
||||||
|
std::string r_str;
|
||||||
|
for (auto row : r) r_str = row.at(0).c_str();
|
||||||
|
|
||||||
|
work.commit();
|
||||||
|
m_data_connections[connection_name].release(c);
|
||||||
|
|
||||||
|
result = JSON::parse(r_str);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
hack::exception ex;
|
||||||
|
ex.description("database dont execute query");
|
||||||
|
ex.system_error(e);
|
||||||
|
ex.params("connection_name", connection_name);
|
||||||
|
ex.params("query", query);
|
||||||
|
ex.params("result", result);
|
||||||
|
|
||||||
|
if (connection_name == var::LOG_CONNECTION)
|
||||||
|
{
|
||||||
|
hack::error()("WARNING!!! ERROR LOG TO DATABASE");
|
||||||
|
hack::error()("query", query);
|
||||||
|
hack::error()(e.what());
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
hack::exception ex;
|
||||||
|
ex.description(var::ALIEN_SYSTEM_ERROR);
|
||||||
|
ex.message(var::EXECUTE_ERROR);
|
||||||
|
ex.params("connection_name", connection_name);
|
||||||
|
ex.params("query", query);
|
||||||
|
ex.params("result", result);
|
||||||
|
|
||||||
|
if (connection_name == var::LOG_CONNECTION)
|
||||||
|
{
|
||||||
|
hack::error()("WARNING!!! ERROR LOG TO DATABASE");
|
||||||
|
hack::error()("query", query);
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
66
src/pgxx/pool_connection.hpp
Normal file
66
src/pgxx/pool_connection.hpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
#include <pqxx/pqxx>
|
||||||
|
|
||||||
|
namespace pgxx
|
||||||
|
{
|
||||||
|
class pool_connection
|
||||||
|
{
|
||||||
|
using connection = std::unique_ptr<pqxx::connection>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
pool_connection() = default;
|
||||||
|
pool_connection(int connection_count, std::string connection_url)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < connection_count; ++i)
|
||||||
|
{
|
||||||
|
auto c = std::make_unique<pqxx::connection>(connection_url);
|
||||||
|
m_connections.push(std::move(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~pool_connection()
|
||||||
|
{
|
||||||
|
while (!m_connections.empty())
|
||||||
|
{
|
||||||
|
auto c = std::move(m_connections.front());
|
||||||
|
m_connections.pop();
|
||||||
|
c->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
pool_connection& operator=(pool_connection&& pc) noexcept
|
||||||
|
{
|
||||||
|
if (this != &pc)
|
||||||
|
m_connections = std::move(pc.m_connections);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
connection get()
|
||||||
|
{
|
||||||
|
std::unique_lock l { m_connections_mutex };
|
||||||
|
m_connections_cond.wait(l, [this]() { return !m_connections.empty(); });
|
||||||
|
|
||||||
|
auto manager = std::move(m_connections.front());
|
||||||
|
m_connections.pop();
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void release(connection& manager)
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_connections_mutex);
|
||||||
|
m_connections.push(std::move(manager));
|
||||||
|
m_connections_cond.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex m_connections_mutex;
|
||||||
|
std::condition_variable m_connections_cond;
|
||||||
|
std::queue<connection> m_connections;
|
||||||
|
};
|
||||||
|
}
|
96
src/pgxx/query_builder.hpp
Normal file
96
src/pgxx/query_builder.hpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
#include "hack/string/string_concat_helper.hpp"
|
||||||
|
#include "hack/concepts/concepts.hpp"
|
||||||
|
|
||||||
|
#include "utils/using.hpp"
|
||||||
|
|
||||||
|
namespace pgxx::builder
|
||||||
|
{
|
||||||
|
template<hack::concepts::is_string First>
|
||||||
|
std::string make_one(First f)
|
||||||
|
{
|
||||||
|
f = std::regex_replace(f, std::regex("'"), "[quote]");
|
||||||
|
return std::string("'") + f + std::string("',");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_one(const char* f)
|
||||||
|
{
|
||||||
|
auto f_str = std::string(f);
|
||||||
|
f_str = std::regex_replace(f_str, std::regex("'"), "[quote]");
|
||||||
|
return std::string("'") + f_str + std::string("',");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_one(char f)
|
||||||
|
{
|
||||||
|
return std::string("'") + f + std::string("',");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename First>
|
||||||
|
requires std::integral<First>
|
||||||
|
std::string make_one(First f)
|
||||||
|
{
|
||||||
|
auto f_str = std::to_string(f);
|
||||||
|
f_str = std::regex_replace(f_str, std::regex("'"), "[quote]");
|
||||||
|
return std::string("'") + f_str + std::string("',");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_one(const float f)
|
||||||
|
{
|
||||||
|
auto f_str = std::to_string(f);
|
||||||
|
f_str = std::regex_replace(f_str, std::regex("'"), "[quote]");
|
||||||
|
return f_str + std::string(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_one(int f)
|
||||||
|
{
|
||||||
|
auto f_str = std::to_string(f);
|
||||||
|
f_str = std::regex_replace(f_str, std::regex("'"), "[quote]");
|
||||||
|
return f_str + std::string(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_one(const std::string& f)
|
||||||
|
{
|
||||||
|
auto f_str = f;
|
||||||
|
f_str = std::regex_replace(f_str, std::regex("'"), "[quote]");
|
||||||
|
return hack::string::str_concat + "'" + f_str + "'::jsonb,";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_one(const JSON& f)
|
||||||
|
{
|
||||||
|
auto f_str = f.dump();
|
||||||
|
f_str = std::regex_replace(f_str, std::regex("'"), "[quote]");
|
||||||
|
return hack::string::str_concat + "'" + f_str + "'::jsonb,";
|
||||||
|
}
|
||||||
|
|
||||||
|
// это заглушкa при компиляции пустых данных
|
||||||
|
template<typename... Args>
|
||||||
|
std::string make() { return ""; }
|
||||||
|
|
||||||
|
template<typename First, typename... Args>
|
||||||
|
std::string make(const First f, const Args... args)
|
||||||
|
{
|
||||||
|
auto param = make_one(f);
|
||||||
|
param += make(args...);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
std::string make_query(const std::string func, const Args... args)
|
||||||
|
{
|
||||||
|
std::string query = "SELECT s_func." + func + "(";
|
||||||
|
query += make(args...);
|
||||||
|
query.replace(query.find_last_of(','), 1, "");
|
||||||
|
query += ");";
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string make_query(const std::string func)
|
||||||
|
{
|
||||||
|
std::string query = "SELECT s_func." + func + "();";
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
3
src/pgxx/utils/define.hpp
Normal file
3
src/pgxx/utils/define.hpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define PGXX() pgxx::manager::instance()
|
8
src/pgxx/utils/using.hpp
Normal file
8
src/pgxx/utils/using.hpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
|
namespace pgxx
|
||||||
|
{
|
||||||
|
using JSON = nlohmann::json;
|
||||||
|
}
|
13
src/pgxx/utils/var.hpp
Normal file
13
src/pgxx/utils/var.hpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace pgxx::var
|
||||||
|
{
|
||||||
|
inline const std::string ALIEN_SYSTEM_ERROR = "is alien system error it can be very dangerous!!! good luck my friend!";
|
||||||
|
inline const std::string NO_VALID_DATA = "no valid data";
|
||||||
|
inline const std::string EXECUTE_ERROR = "execute error";
|
||||||
|
inline const std::string HEADER_FLAG_JSON = "application/json";
|
||||||
|
inline const std::string FUNC_LOGGER = "logger";
|
||||||
|
inline const std::string LOG_CONNECTION = "log_db";
|
||||||
|
}
|
6
subprojects/hack.wrap
Executable file
6
subprojects/hack.wrap
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
[wrap-git]
|
||||||
|
url = https://gitcast.ru/chatlanin/hack.git
|
||||||
|
revision = master
|
||||||
|
|
||||||
|
[provide]
|
||||||
|
hack = hack_dep
|
10
subprojects/nlohmann_json.wrap
Normal file
10
subprojects/nlohmann_json.wrap
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[wrap-file]
|
||||||
|
directory = nlohmann_json-3.11.2
|
||||||
|
lead_directory_missing = true
|
||||||
|
source_url = https://github.com/nlohmann/json/releases/download/v3.11.2/include.zip
|
||||||
|
source_filename = nlohmann_json-3.11.2.zip
|
||||||
|
source_hash = e5c7a9f49a16814be27e4ed0ee900ecd0092bfb7dbfca65b5a421b774dccaaed
|
||||||
|
wrapdb_version = 3.11.2-1
|
||||||
|
|
||||||
|
[provide]
|
||||||
|
nlohmann_json = nlohmann_json_dep
|
47
tests/main.cpp
Normal file
47
tests/main.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include <thread>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "pgxx/pgxx.hpp"
|
||||||
|
|
||||||
|
auto main(int argc, char* args[]) -> int
|
||||||
|
{
|
||||||
|
const std::string con = "";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PGXX().init("con_1", 300, con);
|
||||||
|
PGXX().init("con_2", 300, con);
|
||||||
|
}
|
||||||
|
catch(hack::exception& ex)
|
||||||
|
{
|
||||||
|
ex.log();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pgxx::JSON j {
|
||||||
|
// {
|
||||||
|
// "params", { { "key_1", 1 }, { "key2", "value" } }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// for (auto i = 0; i < 1'000; ++i)
|
||||||
|
// {
|
||||||
|
// std::thread th([&j](){
|
||||||
|
// auto query = PGXX().prepare("read_and_write", j);
|
||||||
|
// auto r = PGXX().execute("con_1", query);
|
||||||
|
// });
|
||||||
|
// th.detach();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// for (auto i = 0; i < 1'000; ++i)
|
||||||
|
// {
|
||||||
|
// std::thread th([&j](){
|
||||||
|
// auto query = PGXX().prepare("read_and_write", j);
|
||||||
|
// auto r = PGXX().execute("con_2", query);
|
||||||
|
// });
|
||||||
|
// th.detach();
|
||||||
|
// }
|
||||||
|
|
||||||
|
hack::log()("ok");
|
||||||
|
}
|
8
tests/meson.build
Normal file
8
tests/meson.build
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
executable(
|
||||||
|
meson.project_name(),
|
||||||
|
'main.cpp',
|
||||||
|
dependencies : deps,
|
||||||
|
cpp_args: args
|
||||||
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user