added logger
This commit is contained in:
parent
1dbe4e6096
commit
72bc6db6b3
|
|
@ -7,6 +7,8 @@
|
|||
#include "matador/sql/query_result.hpp"
|
||||
#include "matador/sql/record.hpp"
|
||||
|
||||
#include "matador/utils/logger.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::sql {
|
||||
|
|
@ -32,21 +34,13 @@ public:
|
|||
[[nodiscard]] record describe(const std::string &table_name) const;
|
||||
[[nodiscard]] bool exists(const std::string &table_name) const;
|
||||
|
||||
template<class Type>
|
||||
query_result<Type> fetch(const std::string &sql)
|
||||
{
|
||||
return query_result<Type>(connection_->fetch(sql));
|
||||
}
|
||||
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(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:
|
||||
connection_info connection_info_;
|
||||
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(column &&col, const std::string &as);
|
||||
column count(const std::string &column);
|
||||
column count_all();
|
||||
|
||||
enum class join_type_t {
|
||||
INNER, OUTER, LEFT, RIGHT
|
||||
|
|
|
|||
|
|
@ -54,8 +54,7 @@ public:
|
|||
template<typename Type>
|
||||
Type fetch_value()
|
||||
{
|
||||
auto result = fetch_all();
|
||||
return {};
|
||||
return fetch_one().at(0).as<Type>();
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public:
|
|||
private:
|
||||
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:
|
||||
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 {
|
||||
|
||||
#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 getenv(const char* 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
|
||||
|
|
|
|||
|
|
@ -2,9 +2,22 @@
|
|||
#define QUERY_STRING_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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
|
||||
* with string to.
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ set(UTILS_HEADER
|
|||
../include/matador/utils/os.hpp
|
||||
../include/matador/utils/access.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
|
||||
utils/field_attributes.cpp
|
||||
|
|
@ -66,7 +67,8 @@ set(UTILS_SOURCES
|
|||
utils/library.cpp
|
||||
utils/os.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})
|
||||
target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace matador::sql {
|
|||
|
||||
connection::connection(connection_info info)
|
||||
: connection_info_(std::move(info))
|
||||
, logger_(stdout, "SQL")
|
||||
{
|
||||
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_info_(x.connection_info_)
|
||||
, logger_(x.logger_)
|
||||
{
|
||||
if (x.connection_) {
|
||||
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_info_ = x.connection_info_;
|
||||
logger_ = x.logger_;
|
||||
if (x.connection_) {
|
||||
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
|
||||
{
|
||||
logger_.debug(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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 result(col.name());
|
||||
escape_quotes_in_identifier(result);
|
||||
if (!col.is_function()) {
|
||||
escape_quotes_in_identifier(result);
|
||||
quote_identifier(result);
|
||||
} 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -460,4 +460,9 @@ column count(const std::string &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()
|
||||
{
|
||||
return session_.call_fetch(builder_.compile().sql);
|
||||
return session_.fetch(builder_.compile().sql);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
auto res = c->call_fetch(q.sql);
|
||||
auto res = c->fetch(q.sql);
|
||||
return query_result<record>{std::move(res), q.prototype};
|
||||
}
|
||||
|
||||
|
|
@ -83,13 +83,13 @@ const class dialect &session::dialect() const
|
|||
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();
|
||||
if (!c.valid()) {
|
||||
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 <stdexcept>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
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) {
|
||||
#ifdef _WIN32
|
||||
char var[1024];
|
||||
|
|
@ -46,4 +61,148 @@ std::string error_string(unsigned long error) {
|
|||
return result;
|
||||
}
|
||||
#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 <sstream>
|
||||
|
||||
namespace matador::utils {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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")");
|
||||
}
|
||||
|
||||
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]") {
|
||||
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||
session s(pool);
|
||||
|
|
@ -72,6 +109,51 @@ TEST_CASE("Execute insert record statement", "[session record]") {
|
|||
.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]") {
|
||||
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||
session s(pool);
|
||||
|
|
@ -122,6 +204,53 @@ TEST_CASE("Execute update record statement", "[session record]") {
|
|||
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]") {
|
||||
connection_pool<connection> pool("sqlite://sqlite.db", 4);
|
||||
session s(pool);
|
||||
|
|
@ -147,8 +276,7 @@ TEST_CASE("Execute select statement with order by", "[session record]") {
|
|||
|
||||
auto result = s.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.where("id"_col == 8)
|
||||
.order_by("name").desc()
|
||||
.order_by("name").asc()
|
||||
.fetch_all();
|
||||
|
||||
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());
|
||||
expected_names.pop_front();
|
||||
}
|
||||
REQUIRE(expected_names.empty());
|
||||
|
||||
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"})
|
||||
.from("person")
|
||||
.group_by("age")
|
||||
.order_by("age_count").asc()
|
||||
.order_by("age_count").desc()
|
||||
.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) {
|
||||
REQUIRE(r.at(0).as<int>() == expected_values.front().first);
|
||||
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);
|
||||
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.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.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