added logger
This commit is contained in:
parent
1dbe4e6096
commit
72bc6db6b3
|
|
@ -7,6 +7,8 @@
|
||||||
#include "matador/sql/query_result.hpp"
|
#include "matador/sql/query_result.hpp"
|
||||||
#include "matador/sql/record.hpp"
|
#include "matador/sql/record.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/logger.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
@ -32,21 +34,13 @@ public:
|
||||||
[[nodiscard]] record describe(const std::string &table_name) const;
|
[[nodiscard]] record describe(const std::string &table_name) const;
|
||||||
[[nodiscard]] bool exists(const std::string &table_name) const;
|
[[nodiscard]] bool exists(const std::string &table_name) const;
|
||||||
|
|
||||||
template<class Type>
|
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
|
||||||
query_result<Type> fetch(const std::string &sql)
|
|
||||||
{
|
|
||||||
return query_result<Type>(connection_->fetch(sql));
|
|
||||||
}
|
|
||||||
[[nodiscard]] std::pair<size_t, std::string> execute(const std::string &sql) const;
|
[[nodiscard]] std::pair<size_t, std::string> execute(const std::string &sql) const;
|
||||||
|
|
||||||
private:
|
|
||||||
friend class session;
|
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<query_result_impl> call_fetch(const std::string &sql) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
connection_info connection_info_;
|
connection_info connection_info_;
|
||||||
std::unique_ptr<connection_impl> connection_;
|
std::unique_ptr<connection_impl> connection_;
|
||||||
|
utils::logger logger_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ struct any_type_to_string_visitor
|
||||||
column alias(const std::string &column, const std::string &as);
|
column alias(const std::string &column, const std::string &as);
|
||||||
column alias(column &&col, const std::string &as);
|
column alias(column &&col, const std::string &as);
|
||||||
column count(const std::string &column);
|
column count(const std::string &column);
|
||||||
|
column count_all();
|
||||||
|
|
||||||
enum class join_type_t {
|
enum class join_type_t {
|
||||||
INNER, OUTER, LEFT, RIGHT
|
INNER, OUTER, LEFT, RIGHT
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,7 @@ public:
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
Type fetch_value()
|
Type fetch_value()
|
||||||
{
|
{
|
||||||
auto result = fetch_all();
|
return fetch_one().at(0).as<Type>();
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ public:
|
||||||
private:
|
private:
|
||||||
friend class query_select_finish;
|
friend class query_select_finish;
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<query_result_impl> call_fetch(const std::string &sql) const;
|
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
connection_pool<connection> &pool_;
|
connection_pool<connection> &pool_;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
#ifndef QUERY_LOGGER_HPP
|
||||||
|
#define QUERY_LOGGER_HPP
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace matador::utils {
|
||||||
|
|
||||||
|
enum class log_level
|
||||||
|
{
|
||||||
|
LVL_FATAL, /**< If a serious error occurred use FATAL level */
|
||||||
|
LVL_ERROR, /**< On error use ERROR level */
|
||||||
|
LVL_WARN, /**< Warnings should use WARN level */
|
||||||
|
LVL_INFO, /**< Information should go with INFO level */
|
||||||
|
LVL_DEBUG, /**< Debug output should use DEBUG level */
|
||||||
|
LVL_TRACE, /**< Trace information should use TRACE level */
|
||||||
|
LVL_ALL /**< This level represents all log levels and should be used for logging */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple file logger
|
||||||
|
*/
|
||||||
|
class logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
logger(const std::string &path, std::string source);
|
||||||
|
logger(FILE *file, std::string source);
|
||||||
|
logger(const logger &x);
|
||||||
|
logger& operator=(const logger &x);
|
||||||
|
logger(logger &&x) noexcept;
|
||||||
|
logger& operator=(logger &&x) noexcept;
|
||||||
|
|
||||||
|
template<typename ... Args>
|
||||||
|
void info(const std::string &what, Args const &... args) const { info(what.c_str(), args...); }
|
||||||
|
template<typename ... Args>
|
||||||
|
void info(const char *what, Args const &... args) const { log(log_level::LVL_INFO, what, args...); }
|
||||||
|
|
||||||
|
template<typename ... Args>
|
||||||
|
void debug(const std::string &what, Args const &... args) const { debug(what.c_str(), args...); }
|
||||||
|
template<typename ... Args>
|
||||||
|
void debug(const char *what, Args const &... args) const { log(log_level::LVL_DEBUG, what, args...); }
|
||||||
|
|
||||||
|
template<typename ... Args>
|
||||||
|
void warn(const std::string &what, Args const &... args) const { warn(what.c_str(), args...); }
|
||||||
|
template<typename ... Args>
|
||||||
|
void warn(const char *what, Args const &... args) const { log(log_level::LVL_WARN, what, args...); }
|
||||||
|
|
||||||
|
template<typename ... Args>
|
||||||
|
void error(const std::string &what, Args const &... args) const { error(what.c_str(), args...); }
|
||||||
|
template<typename ... Args>
|
||||||
|
void error(const char *what, Args const &... args) const { log(log_level::LVL_ERROR, what, args...); }
|
||||||
|
|
||||||
|
template<typename ... Args>
|
||||||
|
void fatal(const std::string &what, Args const &... args) const { fatal(what.c_str(), args...); }
|
||||||
|
template<typename ... Args>
|
||||||
|
void fatal(const char *what, Args const &... args) const { log(log_level::LVL_FATAL, what, args...); }
|
||||||
|
|
||||||
|
template<typename ... Args>
|
||||||
|
void log(log_level lvl, const char *what, Args const &... args) const;
|
||||||
|
void log(log_level lvl, const char *message) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void write(const char *message, size_t size) const;
|
||||||
|
void close();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string path_;
|
||||||
|
FILE *stream = nullptr;
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
std::string source_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... ARGS>
|
||||||
|
void logger::log(log_level lvl, const char *what, ARGS const &... args) const
|
||||||
|
{
|
||||||
|
char message_buffer[16384];
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
sprintf_s(message_buffer, 16384, what, args...);
|
||||||
|
#else
|
||||||
|
sprintf(message_buffer, what, args...);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
log(lvl, message_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //QUERY_LOGGER_HPP
|
||||||
|
|
@ -5,12 +5,40 @@
|
||||||
|
|
||||||
namespace matador::utils::os {
|
namespace matador::utils::os {
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
extern char DIR_SEPARATOR;
|
||||||
|
extern const char* DIR_SEPARATOR_STRING;
|
||||||
|
#else
|
||||||
|
extern char DIR_SEPARATOR;
|
||||||
|
extern const char* DIR_SEPARATOR_STRING;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string error_string(unsigned long error);
|
std::string error_string(unsigned long error);
|
||||||
|
|
||||||
std::string getenv(const char* name);
|
std::string getenv(const char* name);
|
||||||
|
|
||||||
[[maybe_unused]] std::string getenv(const std::string &name);
|
[[maybe_unused]] std::string getenv(const std::string &name);
|
||||||
|
|
||||||
|
FILE* fopen(const std::string &path, const char *modes);
|
||||||
|
FILE* fopen(const char *path, const char *modes);
|
||||||
|
|
||||||
|
std::string get_current_dir();
|
||||||
|
|
||||||
|
bool mkdir(const std::string &dirname);
|
||||||
|
bool mkdir(const char *dirname);
|
||||||
|
|
||||||
|
bool chdir(const std::string &dirname);
|
||||||
|
bool chdir(const char *dirname);
|
||||||
|
|
||||||
|
bool rmdir(const std::string &dirname);
|
||||||
|
bool rmdir(const char *dirname);
|
||||||
|
|
||||||
|
bool mkpath(const std::string &path);
|
||||||
|
bool mkpath(const char *path);
|
||||||
|
|
||||||
|
bool rmpath(const std::string &path);
|
||||||
|
bool rmpath(const char *path);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //QUERY_OS_HPP
|
#endif //QUERY_OS_HPP
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,22 @@
|
||||||
#define QUERY_STRING_HPP
|
#define QUERY_STRING_HPP
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace matador::utils {
|
namespace matador::utils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a string by a delimiter and
|
||||||
|
* add the string tokens to a vector. The
|
||||||
|
* size of the vector is returned.
|
||||||
|
*
|
||||||
|
* @param str The string to split.
|
||||||
|
* @param delim The delimiter character.
|
||||||
|
* @param values The result vector.
|
||||||
|
* @return The size of the vector.
|
||||||
|
*/
|
||||||
|
size_t split(const std::string &str, char delim, std::vector<std::string> &values);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces all occurrences of string from in given string
|
* Replaces all occurrences of string from in given string
|
||||||
* with string to.
|
* with string to.
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,8 @@ set(UTILS_HEADER
|
||||||
../include/matador/utils/os.hpp
|
../include/matador/utils/os.hpp
|
||||||
../include/matador/utils/access.hpp
|
../include/matador/utils/access.hpp
|
||||||
../include/matador/utils/identifier.hpp
|
../include/matador/utils/identifier.hpp
|
||||||
../include/matador/utils/cascade_type.hpp)
|
../include/matador/utils/cascade_type.hpp
|
||||||
|
../include/matador/utils/logger.hpp)
|
||||||
|
|
||||||
set(UTILS_SOURCES
|
set(UTILS_SOURCES
|
||||||
utils/field_attributes.cpp
|
utils/field_attributes.cpp
|
||||||
|
|
@ -66,7 +67,8 @@ set(UTILS_SOURCES
|
||||||
utils/library.cpp
|
utils/library.cpp
|
||||||
utils/os.cpp
|
utils/os.cpp
|
||||||
utils/identifier.cpp
|
utils/identifier.cpp
|
||||||
sql/value_extractor.cpp)
|
sql/value_extractor.cpp
|
||||||
|
utils/logger.cpp)
|
||||||
|
|
||||||
add_library(matador STATIC ${SQL_SOURCES} ${SQL_HEADER} ${UTILS_SOURCES} ${UTILS_HEADER})
|
add_library(matador STATIC ${SQL_SOURCES} ${SQL_HEADER} ${UTILS_SOURCES} ${UTILS_HEADER})
|
||||||
target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ namespace matador::sql {
|
||||||
|
|
||||||
connection::connection(connection_info info)
|
connection::connection(connection_info info)
|
||||||
: connection_info_(std::move(info))
|
: connection_info_(std::move(info))
|
||||||
|
, logger_(stdout, "SQL")
|
||||||
{
|
{
|
||||||
connection_.reset(backend_provider::instance().create_connection(connection_info_.type, connection_info_));
|
connection_.reset(backend_provider::instance().create_connection(connection_info_.type, connection_info_));
|
||||||
}
|
}
|
||||||
|
|
@ -20,6 +21,7 @@ connection::connection(const std::string& dns)
|
||||||
|
|
||||||
connection::connection(const connection &x)
|
connection::connection(const connection &x)
|
||||||
: connection_info_(x.connection_info_)
|
: connection_info_(x.connection_info_)
|
||||||
|
, logger_(x.logger_)
|
||||||
{
|
{
|
||||||
if (x.connection_) {
|
if (x.connection_) {
|
||||||
throw std::runtime_error("couldn't copy connection with valid connection impl");
|
throw std::runtime_error("couldn't copy connection with valid connection impl");
|
||||||
|
|
@ -28,6 +30,7 @@ connection::connection(const connection &x)
|
||||||
|
|
||||||
connection &connection::operator=(const connection &x) {
|
connection &connection::operator=(const connection &x) {
|
||||||
connection_info_ = x.connection_info_;
|
connection_info_ = x.connection_info_;
|
||||||
|
logger_ = x.logger_;
|
||||||
if (x.connection_) {
|
if (x.connection_) {
|
||||||
throw std::runtime_error("couldn't copy connection with valid connection impl");
|
throw std::runtime_error("couldn't copy connection with valid connection impl");
|
||||||
}
|
}
|
||||||
|
|
@ -75,11 +78,13 @@ bool connection::exists(const std::string &table_name) const
|
||||||
|
|
||||||
std::pair<size_t, std::string> connection::execute(const std::string &sql) const
|
std::pair<size_t, std::string> connection::execute(const std::string &sql) const
|
||||||
{
|
{
|
||||||
|
logger_.debug(sql);
|
||||||
return {connection_->execute(sql), sql};
|
return {connection_->execute(sql), sql};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<query_result_impl> connection::call_fetch(const std::string &sql) const
|
std::unique_ptr<query_result_impl> connection::fetch(const std::string &sql) const
|
||||||
{
|
{
|
||||||
|
logger_.debug(sql);
|
||||||
return connection_->fetch(sql);
|
return connection_->fetch(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,14 @@ const std::string &dialect::data_type_at(data_type_t type) const
|
||||||
std::string dialect::prepare_identifier(const column &col) const
|
std::string dialect::prepare_identifier(const column &col) const
|
||||||
{
|
{
|
||||||
std::string result(col.name());
|
std::string result(col.name());
|
||||||
escape_quotes_in_identifier(result);
|
|
||||||
if (!col.is_function()) {
|
if (!col.is_function()) {
|
||||||
|
escape_quotes_in_identifier(result);
|
||||||
quote_identifier(result);
|
quote_identifier(result);
|
||||||
} else {
|
} else {
|
||||||
return sql_func_map_.at(col.function()) + "(" + result + ")";
|
result = sql_func_map_.at(col.function()) + "(" + result + ")";
|
||||||
|
}
|
||||||
|
if (!col.alias().empty()) {
|
||||||
|
result += " AS " + col.alias();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -460,4 +460,9 @@ column count(const std::string &column)
|
||||||
return {sql_function_t::COUNT, column};
|
return {sql_function_t::COUNT, column};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
column count_all()
|
||||||
|
{
|
||||||
|
return count("*");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ record query_select_finish::fetch_one()
|
||||||
|
|
||||||
std::unique_ptr<query_result_impl> query_select_finish::fetch()
|
std::unique_ptr<query_result_impl> query_select_finish::fetch()
|
||||||
{
|
{
|
||||||
return session_.call_fetch(builder_.compile().sql);
|
return session_.fetch(builder_.compile().sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
query_intermediate::query_intermediate(session &db, query_builder &query)
|
query_intermediate::query_intermediate(session &db, query_builder &query)
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ query_result<record> session::fetch(const query &q) const
|
||||||
const_cast<column&>(col).type(rit->type());
|
const_cast<column&>(col).type(rit->type());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto res = c->call_fetch(q.sql);
|
auto res = c->fetch(q.sql);
|
||||||
return query_result<record>{std::move(res), q.prototype};
|
return query_result<record>{std::move(res), q.prototype};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,13 +83,13 @@ const class dialect &session::dialect() const
|
||||||
return dialect_;
|
return dialect_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<query_result_impl> session::call_fetch(const std::string &sql) const
|
std::unique_ptr<query_result_impl> session::fetch(const std::string &sql) const
|
||||||
{
|
{
|
||||||
auto c = pool_.acquire();
|
auto c = pool_.acquire();
|
||||||
if (!c.valid()) {
|
if (!c.valid()) {
|
||||||
throw std::logic_error("no database connection available");
|
throw std::logic_error("no database connection available");
|
||||||
}
|
}
|
||||||
return c->call_fetch(sql);
|
return c->fetch(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
#include "matador/utils/logger.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/os.hpp"
|
||||||
|
#include "matador/utils/string.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace matador::utils {
|
||||||
|
|
||||||
|
std::size_t acquire_thread_index(std::thread::id id);
|
||||||
|
|
||||||
|
std::map<log_level, std::string> level_strings = { /* NOLINT */
|
||||||
|
{ log_level::LVL_DEBUG, "DEBUG" },
|
||||||
|
{ log_level::LVL_INFO, "INFO" },
|
||||||
|
{ log_level::LVL_WARN, "WARN" },
|
||||||
|
{ log_level::LVL_ERROR, "ERROR" },
|
||||||
|
{ log_level::LVL_TRACE, "TRACE" }
|
||||||
|
};
|
||||||
|
|
||||||
|
logger::logger(const std::string &path, std::string source)
|
||||||
|
: source_(std::move(source))
|
||||||
|
{
|
||||||
|
std::string filename(path);
|
||||||
|
// find last dir delimiter
|
||||||
|
const char *last = strrchr(path.c_str(), os::DIR_SEPARATOR);
|
||||||
|
if (last != nullptr) {
|
||||||
|
path_.assign(path.data(), last-path.data());
|
||||||
|
} else {
|
||||||
|
path_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last != nullptr) {
|
||||||
|
filename = (last + 1);
|
||||||
|
}
|
||||||
|
// extract base path and extension
|
||||||
|
std::vector<std::string> result;
|
||||||
|
if (utils::split(filename, '.', result) != 2) {
|
||||||
|
throw std::logic_error("split path must consists of two elements");
|
||||||
|
}
|
||||||
|
// get current path
|
||||||
|
auto pwd = os::get_current_dir();
|
||||||
|
// make path
|
||||||
|
os::mkpath(path_);
|
||||||
|
// change into path
|
||||||
|
os::chdir(path_);
|
||||||
|
// create file
|
||||||
|
stream = os::fopen(filename, "a");
|
||||||
|
if (stream == nullptr) {
|
||||||
|
os::chdir(pwd);
|
||||||
|
throw std::logic_error("error opening file");
|
||||||
|
}
|
||||||
|
os::chdir(pwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger::logger(FILE *file, std::string source)
|
||||||
|
: stream(file)
|
||||||
|
, source_(std::move(source))
|
||||||
|
{}
|
||||||
|
|
||||||
|
logger::logger(const logger &x)
|
||||||
|
: path_(x.path_)
|
||||||
|
, stream(x.stream)
|
||||||
|
, source_(x.source_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
logger &logger::operator=(const logger &x)
|
||||||
|
{
|
||||||
|
if (this == &x) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
path_ = x.path_;
|
||||||
|
stream = x.stream;
|
||||||
|
source_ = x.source_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger::logger(logger &&x) noexcept
|
||||||
|
: path_(std::move(x.path_))
|
||||||
|
, stream(x.stream)
|
||||||
|
, source_(std::move(x.source_))
|
||||||
|
{
|
||||||
|
x.stream = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger &logger::operator=(logger &&x) noexcept
|
||||||
|
{
|
||||||
|
path_ = std::move(x.path_);
|
||||||
|
std::swap(stream, x.stream);
|
||||||
|
source_ = std::move(x.source_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void logger::log(log_level lvl, const char *message) const
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto timestamp = system_clock::now();
|
||||||
|
auto coarse = system_clock::to_time_t(timestamp);
|
||||||
|
auto fine = time_point_cast<std::chrono::milliseconds>(timestamp);
|
||||||
|
|
||||||
|
char timestamp_buffer[sizeof "9999-12-31 23:59:59.999"];
|
||||||
|
std::snprintf(timestamp_buffer + std::strftime(timestamp_buffer,
|
||||||
|
sizeof timestamp_buffer - 3,
|
||||||
|
"%F %T.",
|
||||||
|
std::localtime(&coarse)), 4, "%03ld", fine.time_since_epoch().count() % 1000);
|
||||||
|
char buffer[1024];
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
int ret = sprintf_s(buffer, 1024, "%s [Thread %zu] [%-7s] [%s]: %s\n", timestamp_buffer, acquire_thread_index(std::this_thread::get_id()), level_strings[lvl].c_str(), source_.c_str(), message);
|
||||||
|
#else
|
||||||
|
int ret = sprintf(buffer, "%s [Thread %lu] [%-7s] [%s]: %s\n", timestamp_buffer, acquire_thread_index(std::this_thread::get_id()), level_strings[lvl].c_str(), source_.c_str(), message);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> l(mutex_);
|
||||||
|
write(buffer, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void logger::write(const char *message, size_t size) const
|
||||||
|
{
|
||||||
|
fwrite(message, sizeof(char), size, stream);
|
||||||
|
fflush(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void logger::close()
|
||||||
|
{
|
||||||
|
if (stream) {
|
||||||
|
fclose(stream);
|
||||||
|
stream = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t acquire_thread_index(std::thread::id id)
|
||||||
|
{
|
||||||
|
static std::size_t next_index = 0;
|
||||||
|
static std::mutex my_mutex;
|
||||||
|
static std::map<std::thread::id, std::size_t> ids;
|
||||||
|
std::lock_guard<std::mutex> lock(my_mutex);
|
||||||
|
if(ids.find(id) == ids.end()) {
|
||||||
|
ids[id] = next_index++;
|
||||||
|
}
|
||||||
|
return ids[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
163
src/utils/os.cpp
163
src/utils/os.cpp
|
|
@ -1,13 +1,28 @@
|
||||||
#include "matador/utils/os.hpp"
|
#include "matador/utils/os.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#include <direct.h>
|
||||||
|
#include <io.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace matador::utils::os {
|
namespace matador::utils::os {
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
char DIR_SEPARATOR = '\\';
|
||||||
|
const char* DIR_SEPARATOR_STRING = "\\";
|
||||||
|
#else
|
||||||
|
char DIR_SEPARATOR = '/';
|
||||||
|
const char* DIR_SEPARATOR_STRING = "/";
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string getenv(const char *name) {
|
std::string getenv(const char *name) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
char var[1024];
|
char var[1024];
|
||||||
|
|
@ -46,4 +61,148 @@ std::string error_string(unsigned long error) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::string get_current_dir()
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
#ifdef _WIN32
|
||||||
|
char *dir = _getcwd(buffer, 1024);
|
||||||
|
#else
|
||||||
|
char *dir = ::getcwd(buffer, 1024);
|
||||||
|
#endif
|
||||||
|
if (dir == nullptr) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
::strerror_s(buffer, 1023, errno);
|
||||||
|
throw std::logic_error(buffer);
|
||||||
|
#else
|
||||||
|
throw std::logic_error(::strerror(errno));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return {buffer};
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fopen(const std::string &path, const char *modes)
|
||||||
|
{
|
||||||
|
return fopen(path.c_str(), modes);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fopen(const char *path, const char *modes)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return _fsopen(path, modes, _SH_DENYWR);
|
||||||
|
#else
|
||||||
|
return ::fopen(path, modes);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mkdir(const std::string &dirname)
|
||||||
|
{
|
||||||
|
return os::mkdir(dirname.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mkdir(const char *dirname)
|
||||||
|
{
|
||||||
|
if (dirname == nullptr || strlen(dirname) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
return ::_mkdir(dirname) == 0;
|
||||||
|
#else
|
||||||
|
return ::mkdir(dirname, S_IRWXU) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool chdir(const std::string &dirname)
|
||||||
|
{
|
||||||
|
return os::chdir(dirname.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool chdir(const char *dirname)
|
||||||
|
{
|
||||||
|
if (dirname == nullptr || strlen(dirname) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
return _chdir(dirname) == 0;
|
||||||
|
#else
|
||||||
|
return ::chdir(dirname) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmdir(const std::string &dirname)
|
||||||
|
{
|
||||||
|
return os::rmdir(dirname.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmdir(const char *dirname)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return _rmdir(dirname) == 0;
|
||||||
|
#else
|
||||||
|
return ::rmdir(dirname) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mkpath(const std::string &path) {
|
||||||
|
return os::rmpath(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mkpath(const char *path) {
|
||||||
|
char tmp[256];
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
snprintf(tmp, sizeof(tmp),"%s",path);
|
||||||
|
len = strlen(tmp);
|
||||||
|
if (len == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(tmp[len - 1] == DIR_SEPARATOR) {
|
||||||
|
tmp[len - 1] = 0;
|
||||||
|
}
|
||||||
|
for(char *p = tmp + 1; *p; p++) {
|
||||||
|
if (*p == DIR_SEPARATOR) {
|
||||||
|
*p = 0;
|
||||||
|
if (!os::mkdir(tmp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*p = DIR_SEPARATOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os::mkdir(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmpath(const std::string &path)
|
||||||
|
{
|
||||||
|
return os::rmpath(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmpath(const char *path)
|
||||||
|
{
|
||||||
|
// change next to last path segment
|
||||||
|
std::vector<char> pathcopy(path, path+::strlen(path)+1);
|
||||||
|
std::vector<std::string> segments;
|
||||||
|
#ifdef _WIN32
|
||||||
|
char *next_token = nullptr;
|
||||||
|
char *segment = ::strtok_s(pathcopy.data(), DIR_SEPARATOR_STRING, &next_token);
|
||||||
|
#else
|
||||||
|
char *segment = ::strtok(pathcopy.data(), DIR_SEPARATOR_STRING);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (segment != nullptr) {
|
||||||
|
os::chdir(segment);
|
||||||
|
segments.emplace_back(segment);
|
||||||
|
#ifdef _WIN32
|
||||||
|
segment = ::strtok_s(nullptr, DIR_SEPARATOR_STRING, &next_token);
|
||||||
|
#else
|
||||||
|
segment = ::strtok(nullptr, DIR_SEPARATOR_STRING);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
auto first = segments.rbegin();
|
||||||
|
for (auto it=first; it!=segments.rend(); ++it) {
|
||||||
|
os::chdir("..");
|
||||||
|
os::rmdir(*it);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include "matador/utils/string.hpp"
|
#include "matador/utils/string.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace matador::utils {
|
namespace matador::utils {
|
||||||
|
|
||||||
void replace_all(std::string &in, const std::string &from, const std::string &to)
|
void replace_all(std::string &in, const std::string &from, const std::string &to)
|
||||||
|
|
@ -19,4 +21,14 @@ const std::string &to_string(const std::string &str)
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t split(const std::string &str, char delim, std::vector<std::string> &values)
|
||||||
|
{
|
||||||
|
std::stringstream ss(str);
|
||||||
|
std::string item;
|
||||||
|
while (std::getline(ss, item, delim)) {
|
||||||
|
values.push_back(item);
|
||||||
|
}
|
||||||
|
return values.size();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -28,6 +28,43 @@ TEST_CASE("Create and drop table statement", "[session record]") {
|
||||||
REQUIRE(res.second == R"(DROP TABLE "person")");
|
REQUIRE(res.second == R"(DROP TABLE "person")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Create and drop table statement with foreign key", "[session record]") {
|
||||||
|
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||||
|
session s(pool);
|
||||||
|
|
||||||
|
auto res = s.create()
|
||||||
|
.table("airplane", {
|
||||||
|
make_pk_column<unsigned long>("id"),
|
||||||
|
make_column<std::string>("brand", 255),
|
||||||
|
make_column<std::string>("model", 255),
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
REQUIRE(res.second == R"(CREATE TABLE "airplane" ("id" BIGINT NOT NULL, "brand" VARCHAR(255), "model" VARCHAR(255), CONSTRAINT PK_airplane PRIMARY KEY (id)))");
|
||||||
|
|
||||||
|
res = s.create()
|
||||||
|
.table("flight", {
|
||||||
|
make_pk_column<unsigned long>("id"),
|
||||||
|
make_fk_column<unsigned long>("airplane_id", "airplane", "id"),
|
||||||
|
make_column<std::string>("pilot_name", 255),
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
REQUIRE(res.second == R"(CREATE TABLE "flight" ("id" BIGINT NOT NULL, "airplane_id" BIGINT, "pilot_name" VARCHAR(255), CONSTRAINT PK_flight PRIMARY KEY (id), CONSTRAINT FK_flight_airplane_id FOREIGN KEY (airplane_id) REFERENCES airplane(id)))");
|
||||||
|
|
||||||
|
res = s.drop()
|
||||||
|
.table("flight")
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
REQUIRE(res.second == R"(DROP TABLE "flight")");
|
||||||
|
|
||||||
|
res = s.drop()
|
||||||
|
.table("airplane")
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
REQUIRE(res.second == R"(DROP TABLE "airplane")");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Execute insert record statement", "[session record]") {
|
TEST_CASE("Execute insert record statement", "[session record]") {
|
||||||
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||||
session s(pool);
|
session s(pool);
|
||||||
|
|
@ -72,6 +109,51 @@ TEST_CASE("Execute insert record statement", "[session record]") {
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Execute insert record statement with foreign key", "[session record]") {
|
||||||
|
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||||
|
session s(pool);
|
||||||
|
|
||||||
|
auto res = s.create()
|
||||||
|
.table("airplane", {
|
||||||
|
make_pk_column<unsigned long>("id"),
|
||||||
|
make_column<std::string>("brand", 255),
|
||||||
|
make_column<std::string>("model", 255),
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
REQUIRE(res.second == R"(CREATE TABLE "airplane" ("id" BIGINT NOT NULL, "brand" VARCHAR(255), "model" VARCHAR(255), CONSTRAINT PK_airplane PRIMARY KEY (id)))");
|
||||||
|
|
||||||
|
res = s.create()
|
||||||
|
.table("flight", {
|
||||||
|
make_pk_column<unsigned long>("id"),
|
||||||
|
make_fk_column<unsigned long>("airplane_id", "airplane", "id"),
|
||||||
|
make_column<std::string>("pilot_name", 255),
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
REQUIRE(res.second == R"(CREATE TABLE "flight" ("id" BIGINT NOT NULL, "airplane_id" BIGINT, "pilot_name" VARCHAR(255), CONSTRAINT PK_flight PRIMARY KEY (id), CONSTRAINT FK_flight_airplane_id FOREIGN KEY (airplane_id) REFERENCES airplane(id)))");
|
||||||
|
|
||||||
|
res = s.insert().into("airplane", {"id", "brand", "model"}).values({1, "Airbus", "A380"}).execute();
|
||||||
|
REQUIRE(res.first == 1);
|
||||||
|
|
||||||
|
res = s.insert().into("airplane", {"id", "brand", "model"}).values({2, "Boeing", "707"}).execute();
|
||||||
|
REQUIRE(res.first == 1);
|
||||||
|
|
||||||
|
res = s.insert().into("airplane", {"id", "brand", "model"}).values({3, "Boeing", "747"}).execute();
|
||||||
|
REQUIRE(res.first == 1);
|
||||||
|
|
||||||
|
auto count = s.select({count_all()}).from("airplane").fetch_value<int>();
|
||||||
|
REQUIRE(count == 3);
|
||||||
|
|
||||||
|
res = s.insert().into("flight", {"id", "airplane_id", "pilot_name"}).values({4, 1, "George"}).execute();
|
||||||
|
REQUIRE(res.first == 1);
|
||||||
|
|
||||||
|
res = s.drop().table("flight").execute();
|
||||||
|
REQUIRE(res.second == R"(DROP TABLE "flight")");
|
||||||
|
res = s.drop().table("airplane").execute();
|
||||||
|
REQUIRE(res.second == R"(DROP TABLE "airplane")");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Execute update record statement", "[session record]") {
|
TEST_CASE("Execute update record statement", "[session record]") {
|
||||||
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||||
session s(pool);
|
session s(pool);
|
||||||
|
|
@ -122,6 +204,53 @@ TEST_CASE("Execute update record statement", "[session record]") {
|
||||||
s.drop().table("person").execute();
|
s.drop().table("person").execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Execute select statement", "[session record]") {
|
||||||
|
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||||
|
session s(pool);
|
||||||
|
|
||||||
|
auto res = s.create()
|
||||||
|
.table("person", {
|
||||||
|
make_pk_column<unsigned long>("id"),
|
||||||
|
make_column<std::string>("name", 255),
|
||||||
|
make_column<unsigned short>("age")
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
REQUIRE(res.first == 0);
|
||||||
|
|
||||||
|
res = s.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
|
||||||
|
REQUIRE(res.first == 1);
|
||||||
|
res = s.insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute();
|
||||||
|
REQUIRE(res.first == 1);
|
||||||
|
res = s.insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute();
|
||||||
|
REQUIRE(res.first == 1);
|
||||||
|
res = s.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
|
||||||
|
REQUIRE(res.first == 1);
|
||||||
|
|
||||||
|
auto result = s.select({"id", "name", "age"})
|
||||||
|
.from("person")
|
||||||
|
.fetch_all();
|
||||||
|
|
||||||
|
std::list<std::string> expected_names {"george", "jane", "michael", "bob"};
|
||||||
|
for (const auto &p : result) {
|
||||||
|
REQUIRE(p.at(1).str() == expected_names.front());
|
||||||
|
expected_names.pop_front();
|
||||||
|
}
|
||||||
|
REQUIRE(expected_names.empty());
|
||||||
|
|
||||||
|
auto rec = s.select({"id", "name", "age"})
|
||||||
|
.from("person")
|
||||||
|
.fetch_one();
|
||||||
|
REQUIRE(rec.at(1).str() == "george");
|
||||||
|
|
||||||
|
auto name = s.select({"name"})
|
||||||
|
.from("person")
|
||||||
|
.fetch_value<std::string>();
|
||||||
|
REQUIRE(name == "george");
|
||||||
|
|
||||||
|
s.drop().table("person").execute();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Execute select statement with order by", "[session record]") {
|
TEST_CASE("Execute select statement with order by", "[session record]") {
|
||||||
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||||
session s(pool);
|
session s(pool);
|
||||||
|
|
@ -147,8 +276,7 @@ TEST_CASE("Execute select statement with order by", "[session record]") {
|
||||||
|
|
||||||
auto result = s.select({"id", "name", "age"})
|
auto result = s.select({"id", "name", "age"})
|
||||||
.from("person")
|
.from("person")
|
||||||
.where("id"_col == 8)
|
.order_by("name").asc()
|
||||||
.order_by("name").desc()
|
|
||||||
.fetch_all();
|
.fetch_all();
|
||||||
|
|
||||||
std::list<std::string> expected_names {"bob", "george", "jane", "michael"};
|
std::list<std::string> expected_names {"bob", "george", "jane", "michael"};
|
||||||
|
|
@ -156,6 +284,7 @@ TEST_CASE("Execute select statement with order by", "[session record]") {
|
||||||
REQUIRE(p.at(1).str() == expected_names.front());
|
REQUIRE(p.at(1).str() == expected_names.front());
|
||||||
expected_names.pop_front();
|
expected_names.pop_front();
|
||||||
}
|
}
|
||||||
|
REQUIRE(expected_names.empty());
|
||||||
|
|
||||||
s.drop().table("person").execute();
|
s.drop().table("person").execute();
|
||||||
}
|
}
|
||||||
|
|
@ -186,10 +315,10 @@ TEST_CASE("Execute select statement with group by and order by", "[session recor
|
||||||
auto result = s.select({alias(count("age"), "age_count"), "age"})
|
auto result = s.select({alias(count("age"), "age_count"), "age"})
|
||||||
.from("person")
|
.from("person")
|
||||||
.group_by("age")
|
.group_by("age")
|
||||||
.order_by("age_count").asc()
|
.order_by("age_count").desc()
|
||||||
.fetch_all();
|
.fetch_all();
|
||||||
|
|
||||||
std::list<std::pair<int, int>> expected_values {{2, 13}, {2, 45}, {1, 67}};
|
std::list<std::pair<int, int>> expected_values {{2, 45}, {2, 13}, {1, 67}};
|
||||||
for (const auto &r : result) {
|
for (const auto &r : result) {
|
||||||
REQUIRE(r.at(0).as<int>() == expected_values.front().first);
|
REQUIRE(r.at(0).as<int>() == expected_values.front().first);
|
||||||
REQUIRE(r.at(1).as<int>() == expected_values.front().second);
|
REQUIRE(r.at(1).as<int>() == expected_values.front().second);
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,15 @@ TEST_CASE("Create table with foreign key relation", "[session]") {
|
||||||
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||||
session s(pool);
|
session s(pool);
|
||||||
|
|
||||||
auto res = s.create().table<airplane>("airplane").execute();
|
auto res = s.create()
|
||||||
|
.table<airplane>("airplane")
|
||||||
|
.execute();
|
||||||
REQUIRE(res.first == 0);
|
REQUIRE(res.first == 0);
|
||||||
REQUIRE(res.second == R"(CREATE TABLE "airplane" ("id" BIGINT, "brand" VARCHAR(255), "model" VARCHAR(255), CONSTRAINT PK_airplane PRIMARY KEY (id)))");
|
REQUIRE(res.second == R"(CREATE TABLE "airplane" ("id" BIGINT, "brand" VARCHAR(255), "model" VARCHAR(255), CONSTRAINT PK_airplane PRIMARY KEY (id)))");
|
||||||
|
|
||||||
res = s.create().table<flight>("flight").execute();
|
res = s.create()
|
||||||
|
.table<flight>("flight")
|
||||||
|
.execute();
|
||||||
REQUIRE(res.first == 0);
|
REQUIRE(res.first == 0);
|
||||||
REQUIRE(res.second == R"(CREATE TABLE "flight" ("id" BIGINT, "airplane_id" BIGINT, "pilot_name" VARCHAR(255), CONSTRAINT PK_flight PRIMARY KEY (id), CONSTRAINT FK_flight_airplane_id FOREIGN KEY (airplane_id) REFERENCES airplane(id)))");
|
REQUIRE(res.second == R"(CREATE TABLE "flight" ("id" BIGINT, "airplane_id" BIGINT, "pilot_name" VARCHAR(255), CONSTRAINT PK_flight PRIMARY KEY (id), CONSTRAINT FK_flight_airplane_id FOREIGN KEY (airplane_id) REFERENCES airplane(id)))");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue