diff --git a/backends/sqlite/CMakeLists.txt b/backends/sqlite/CMakeLists.txt index ce8aaa3..412dc02 100644 --- a/backends/sqlite/CMakeLists.txt +++ b/backends/sqlite/CMakeLists.txt @@ -1,11 +1,13 @@ set(HEADER include/sqlite_connection.hpp include/sqlite_error.hpp + include/sqlite_dialect.hpp ) set(SOURCES src/sqlite_connection.cpp src/sqlite_error.cpp + src/sqlite_dialect.cpp ) add_library(matador-sqlite SHARED ${SOURCES} ${HEADER}) diff --git a/backends/sqlite/include/sqlite_dialect.hpp b/backends/sqlite/include/sqlite_dialect.hpp new file mode 100644 index 0000000..61e065c --- /dev/null +++ b/backends/sqlite/include/sqlite_dialect.hpp @@ -0,0 +1,12 @@ +#ifndef QUERY_SQLITE_DIALECT_HPP +#define QUERY_SQLITE_DIALECT_HPP + +#include "matador/sql/dialect.hpp" + +namespace matador::backends::sqlite { + +} + +extern "C" [[maybe_unused]] const matador::sql::dialect* get_dialect(); + +#endif //QUERY_SQLITE_DIALECT_HPP diff --git a/backends/sqlite/src/sqlite_dialect.cpp b/backends/sqlite/src/sqlite_dialect.cpp new file mode 100644 index 0000000..ed6fc48 --- /dev/null +++ b/backends/sqlite/src/sqlite_dialect.cpp @@ -0,0 +1,11 @@ +#include "sqlite_dialect.hpp" + +[[maybe_unused]] const matador::sql::dialect* get_dialect() { + using namespace matador::sql; + static dialect d {{ + { dialect::token_t::BEGIN, "BEGIN TRANSACTION"}, + { dialect::token_t::COMMIT, "COMMIT TRANSACTION"}, + { dialect::token_t::ROLLBACK, "ROLLBACK TRANSACTION"} + }}; + return &d; +} diff --git a/include/matador/sql/backend_provider.hpp b/include/matador/sql/backend_provider.hpp new file mode 100644 index 0000000..dc07e70 --- /dev/null +++ b/include/matador/sql/backend_provider.hpp @@ -0,0 +1,43 @@ +#ifndef QUERY_BACKEND_PROVIDER_HPP +#define QUERY_BACKEND_PROVIDER_HPP + +#include "matador/utils/library.hpp" + +#include +#include + +namespace matador::sql { + +class connection_impl; +class dialect; + +class backend_provider +{ +public: + explicit backend_provider(std::string backends_path); + + connection_impl* create_connection(const std::string &connection_type); + const dialect& connection_dialect(const std::string &connection_type); + +private: + struct backend_context { + backend_context(const std::string &connection_type, + const std::string &backends_path); + ~backend_context(); + + typedef connection_impl*(*create_func)(); + typedef void (*destroy_func)(connection_impl*); + typedef const dialect*(*dialect_func)(); + + create_func create_connection{}; + destroy_func destroy_connection{}; + dialect_func get_dialect{}; + utils::library lib; + }; +private: + using backends_t = std::unordered_map; + backends_t backends_; + std::string backends_path_; +}; +} +#endif //QUERY_BACKEND_PROVIDER_HPP diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp index c3de605..c5ec78d 100644 --- a/include/matador/sql/dialect.hpp +++ b/include/matador/sql/dialect.hpp @@ -10,7 +10,7 @@ namespace matador::sql { -class [[nodiscard]] dialect +class dialect { public: enum class token_t : uint8_t @@ -59,7 +59,15 @@ public: ESCAPE_CLOSING_BRACKET /**< The escape quotes differ; escape the closing one */ }; + using token_to_string_map = std::unordered_map; + using data_type_to_string_map = std::unordered_map; + public: + dialect() = default; + dialect(const token_to_string_map &token_replace_map, const data_type_to_string_map &data_type_replace_map); + explicit dialect(const data_type_to_string_map &data_type_replace_map); + explicit dialect(const token_to_string_map &token_replace_map); + const std::string& token_at(token_t token) const; const std::string& data_type_at(data_type_t type) const; @@ -124,8 +132,6 @@ public: const std::vector& columns() const; private: - using token_to_string_map = std::unordered_map; - std::vector host_vars_; std::vector columns_; @@ -167,7 +173,6 @@ private: {token_t::NONE, ""} }; - using data_type_to_string_map = std::unordered_map; data_type_to_string_map data_types_ { {data_type_t::type_char, "TINYINT"}, {data_type_t::type_short, "SMALLINT"}, diff --git a/include/matador/utils/os.hpp b/include/matador/utils/os.hpp new file mode 100644 index 0000000..645dacf --- /dev/null +++ b/include/matador/utils/os.hpp @@ -0,0 +1,14 @@ +#ifndef QUERY_OS_HPP +#define QUERY_OS_HPP + +#include + +namespace matador::utils::os { + +std::string getenv(const char* name); + +[[maybe_unused]] std::string getenv(const std::string &name); + +} + +#endif //QUERY_OS_HPP diff --git a/main.cpp b/main.cpp index 66df86e..c7c4e36 100644 --- a/main.cpp +++ b/main.cpp @@ -1,21 +1,17 @@ -#include -#include -#include - +#include #include - -using namespace matador; +#include int main() { - sql::dialect d; - - sql::query_builder query(d); - - std::cout << query.create().table("person", { - { "id", sql::data_type_traits::builtin_type() }, - { "name", sql::data_type_traits::builtin_type(255), 255 }, - { "id", sql::data_type_traits::builtin_type() } - }).compile(); + const std::string env_var {"MATADOR_BACKENDS_PATH"}; +// char var[1024]; +// size_t len{}; +// const auto error = getenv_s(&len, var, 1024, env_var.c_str()); +// if (error > 0) { +// std::cout << "error: unknown env var " << env_var << "\n"; +// } else { +// std::cout << "env var: " << var << "\n"; +// } return 0; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c9d8316..8850590 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,7 +9,8 @@ set(SQL_SOURCES sql/record.cpp sql/connection_info.cpp sql/connection_impl.cpp - sql/session.cpp) + sql/session.cpp + sql/backend_provider.cpp) set(SQL_HEADER ../include/matador/sql/dialect.hpp @@ -27,19 +28,22 @@ set(SQL_HEADER ../include/matador/sql/connection_impl.hpp ../include/matador/sql/connection_info.hpp ../include/matador/sql/connection_pool.hpp - ../include/matador/sql/session.hpp) + ../include/matador/sql/session.hpp + ../include/matador/sql/backend_provider.hpp) set(UTILS_HEADER ../include/matador/utils/field_attributes.hpp ../include/matador/utils/string.hpp ../include/matador/utils/constraints.hpp - ../include/matador/utils/library.hpp) + ../include/matador/utils/library.hpp + ../include/matador/utils/os.hpp) set(UTILS_SOURCES utils/field_attributes.cpp utils/string.cpp sql/condition.cpp - utils/library.cpp) + utils/library.cpp + utils/os.cpp) add_library(matador STATIC ${SQL_SOURCES} ${SQL_HEADER} ${UTILS_SOURCES} ${UTILS_HEADER}) target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include) diff --git a/src/sql/backend_provider.cpp b/src/sql/backend_provider.cpp new file mode 100644 index 0000000..9160e2a --- /dev/null +++ b/src/sql/backend_provider.cpp @@ -0,0 +1,46 @@ +#include "matador/sql/backend_provider.hpp" + +#include +#include +#include + +namespace matador::sql { +backend_provider::backend_provider(std::string backends_path) +: backends_path_(std::move(backends_path)) {} + +connection_impl *backend_provider::create_connection(const std::string &connection_type) +{ + auto it = backends_.find(connection_type); + if (it == backends_.end()) { + it = backends_.emplace(connection_type, backend_context{connection_type, backends_path_}).first; + } + return (*it->second.create_connection)(); +} + +const dialect &backend_provider::connection_dialect(const std::string &connection_type) +{ + auto it = backends_.find(connection_type); + if (it == backends_.end()) { + it = backends_.emplace(connection_type, backend_context{connection_type, backends_path_}).first; + } + return *(*it->second.get_dialect)(); +} + +backend_provider::backend_context::backend_context(const std::string &connection_type, + const std::string &backends_path) +{ + if (!lib.load(backends_path + "matador-" + connection_type)) { + throw std::runtime_error("couldn't load library '" + connection_type + "'"); + } + + create_connection = reinterpret_cast(reinterpret_cast(lib.function("create_database"))); + destroy_connection = reinterpret_cast(reinterpret_cast(lib.function("destroy_database"))); + get_dialect = reinterpret_cast(reinterpret_cast(lib.function("get_dialect"))); +} + +backend_provider::backend_context::~backend_context() +{ + lib.unload(); +} + +} \ No newline at end of file diff --git a/src/sql/dialect.cpp b/src/sql/dialect.cpp index ae7f194..666d4e5 100644 --- a/src/sql/dialect.cpp +++ b/src/sql/dialect.cpp @@ -3,6 +3,26 @@ #include "matador/utils/string.hpp" namespace matador::sql { + +dialect::dialect(const dialect::token_to_string_map &token_replace_map, const dialect::data_type_to_string_map &data_type_replace_map) +{ + for (const auto &token : token_replace_map) { + tokens_[token.first] = token.second; + } + + for (const auto &data_type : data_type_replace_map) { + data_types_[data_type.first] = data_type.second; + } +} + +dialect::dialect(const dialect::data_type_to_string_map &data_type_replace_map) +: dialect({}, data_type_replace_map) +{} + +dialect::dialect(const dialect::token_to_string_map &token_replace_map) +: dialect(token_replace_map, {}) +{} + const std::string& dialect::token_at(dialect::token_t token) const { return tokens_.at(token); diff --git a/src/utils/os.cpp b/src/utils/os.cpp new file mode 100644 index 0000000..561f50b --- /dev/null +++ b/src/utils/os.cpp @@ -0,0 +1,24 @@ +#include "matador/utils/os.hpp" + +#include + +namespace matador::utils::os { + +std::string getenv(const char *name) { +#ifdef _WIN32 + char var[1024]; + size_t len{}; + const auto error = getenv_s(&len, var, 1024, name); + if (error > 0) { + throw std::logic_error("failed to get environment variable"); + }; + return var; +#else + return ::getenv(name); +#endif +} + +[[maybe_unused]] std::string getenv(const std::string &name) { + return getenv(name.c_str()); +} +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4d5bf8e..b7610dc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,11 @@ add_executable(tests builder.cpp session.cpp record.cpp connection_pool.cpp - query.cpp) -target_link_libraries(tests PRIVATE Catch2::Catch2WithMain matador) + query.cpp + backend_provider.cpp) +target_link_libraries(tests PRIVATE + Catch2::Catch2WithMain + matador + ${CMAKE_DL_LIBS} + ${SQLite3_LIBRARIES}) target_include_directories(tests PUBLIC $/include) \ No newline at end of file diff --git a/test/backend_provider.cpp b/test/backend_provider.cpp new file mode 100644 index 0000000..cbd6e67 --- /dev/null +++ b/test/backend_provider.cpp @@ -0,0 +1,20 @@ +#include + +#include "matador/sql/backend_provider.hpp" + +#include "matador/utils/os.hpp" + +using namespace matador::sql; + +TEST_CASE("Load backend", "[backend provider]") { + auto path = matador::utils::os::getenv("MATADOR_BACKENDS_PATH"); + if (path.back() != '\\') { + path.push_back('\\'); + } + + REQUIRE(!path.empty()); + + backend_provider provider(path); + + const auto &d = provider.connection_dialect("sqlite"); +} \ No newline at end of file