diff --git a/.github/workflows/linux-cxx20-conan.yaml b/.github/workflows/linux-cxx20-conan.yaml index 7792853..1395061 100644 --- a/.github/workflows/linux-cxx20-conan.yaml +++ b/.github/workflows/linux-cxx20-conan.yaml @@ -2,6 +2,7 @@ name: linux-cxx20-conan on: [push, pull_request] + jobs: linux: strategy: @@ -35,6 +36,9 @@ jobs: compiler-version: 12 link: "shared" name: "${{ github.job }} (${{ matrix.compiler }}-${{ matrix.compiler-version }}-${{ matrix.link }})" + concurrency: + group: ci-${{ github.ref }}-${{ github.job }}-${{ matrix.compiler }}-${{ matrix.compiler-version }}-${{ matrix.link }} + cancel-in-progress: true runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/linux-cxx20-vcpkg.yaml b/.github/workflows/linux-cxx20-vcpkg.yaml index b7d26e1..6adda0e 100644 --- a/.github/workflows/linux-cxx20-vcpkg.yaml +++ b/.github/workflows/linux-cxx20-vcpkg.yaml @@ -2,6 +2,7 @@ name: linux-cxx20-vcpkg on: [push, pull_request] + env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" @@ -60,6 +61,9 @@ jobs: compiler-version: 14 db: mysql name: "${{ github.job }} (${{ matrix.compiler }}-${{ matrix.compiler-version }}-${{ matrix.db }})" + concurrency: + group: ci-${{ github.ref }}-${{ github.job }}-${{ matrix.compiler }}-${{ matrix.compiler-version }}-${{ matrix.db }} + cancel-in-progress: true runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/macos-cxx20-conan.yaml b/.github/workflows/macos-cxx20-conan.yaml index cb88eae..b01bb41 100644 --- a/.github/workflows/macos-cxx20-conan.yaml +++ b/.github/workflows/macos-cxx20-conan.yaml @@ -2,6 +2,7 @@ name: macos-cxx20-conan on: [push, pull_request] + jobs: macos-clang: strategy: @@ -17,6 +18,9 @@ jobs: - os: "macos-13" link: "shared" name: "${{ github.job }} (${{ matrix.os }}-${{ matrix.link }})" + concurrency: + group: ci-${{ github.ref }}-${{ github.job }}-${{ matrix.os }}-${{ matrix.link }} + cancel-in-progress: true runs-on: ${{ matrix.os }} steps: - name: Checkout diff --git a/.github/workflows/macos-cxx20-vcpkg.yaml b/.github/workflows/macos-cxx20-vcpkg.yaml index 72e636a..b4e7c37 100644 --- a/.github/workflows/macos-cxx20-vcpkg.yaml +++ b/.github/workflows/macos-cxx20-vcpkg.yaml @@ -2,6 +2,7 @@ name: macos-cxx20-vcpkg on: [push, pull_request] + env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" @@ -24,6 +25,9 @@ jobs: - os: "macos-13" db: mysql name: "${{ github.job }} (${{ matrix.os }}-${{ matrix.db }})" + concurrency: + group: ci-${{ github.ref }}-${{ github.job }}-${{ matrix.os }}-${{ matrix.db }} + cancel-in-progress: true runs-on: ${{ matrix.os }} steps: - name: Checkout diff --git a/.github/workflows/windows-cxx20-vcpkg.yaml b/.github/workflows/windows-cxx20-vcpkg.yaml index 4785be7..f8a2f71 100644 --- a/.github/workflows/windows-cxx20-vcpkg.yaml +++ b/.github/workflows/windows-cxx20-vcpkg.yaml @@ -14,7 +14,11 @@ jobs: - db: postgres - db: sqlite - db: mysql + - db: headers name: "(windows-${{ matrix.db }})" + concurrency: + group: ci-${{ github.ref }}-windows-${{ matrix.db }} + cancel-in-progress: true runs-on: windows-latest steps: - name: Checkout @@ -30,21 +34,27 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - uses: ilammy/msvc-dev-cmd@v1 - uses: lukka/run-vcpkg@v11 + - name: Compile + if: matrix.db == 'headers' + run: | + cmake -S . -B build -DCMAKE_CXX_STANDARD=20 -DSQLGEN_CHECK_HEADERS=ON + cmake --build build --config Release -j4 - name: Compile if: matrix.db == 'postgres' run: | - cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=20 -DSQLGEN_BUILD_TESTS=ON -DSQLGEN_SQLITE3=OFF -DSQLGEN_BUILD_DRY_TESTS_ONLY=ON + cmake -S . -B build -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Release -DSQLGEN_BUILD_TESTS=ON -DSQLGEN_SQLITE3=OFF -DSQLGEN_BUILD_DRY_TESTS_ONLY=ON -DBUILD_SHARED_LIBS=ON -DVCPKG_TARGET_TRIPLET=x64-windows-release cmake --build build --config Release -j4 - name: Compile if: matrix.db == 'sqlite' run: | - cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=20 -DSQLGEN_BUILD_TESTS=ON -DSQLGEN_POSTGRES=OFF -DSQLGEN_CHECK_HEADERS=ON + cmake -S . -B build -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Release -DSQLGEN_BUILD_TESTS=ON -DSQLGEN_POSTGRES=OFF -DBUILD_SHARED_LIBS=ON -DVCPKG_TARGET_TRIPLET=x64-windows-release cmake --build build --config Release -j4 - name: Compile if: matrix.db == 'mysql' run: | - cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=20 -DSQLGEN_BUILD_TESTS=ON -DSQLGEN_MYSQL=ON -DSQLGEN_POSTGRES=OFF -DSQLGEN_SQLITE3=OFF -DSQLGEN_BUILD_DRY_TESTS_ONLY=ON + cmake -S . -B build -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Release -DSQLGEN_BUILD_TESTS=ON -DSQLGEN_MYSQL=ON -DSQLGEN_POSTGRES=OFF -DSQLGEN_SQLITE3=OFF -DSQLGEN_BUILD_DRY_TESTS_ONLY=ON -DBUILD_SHARED_LIBS=ON -DVCPKG_TARGET_TRIPLET=x64-windows-release cmake --build build --config Release -j4 - name: Run tests + if: matrix.db != 'headers' run: | ctest --test-dir build --output-on-failure diff --git a/.gitignore b/.gitignore index 9fe31ab..27492ce 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ *.out *.app +.cache/ build/ vcpkg_installed/ CMakeUserPresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 25967ee..7a2237d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.23) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + option(SQLGEN_BUILD_SHARED "Build shared library" ${BUILD_SHARED_LIBS}) option(SQLGEN_MYSQL "Enable MySQL support" OFF) @@ -43,7 +45,7 @@ if (SQLGEN_USE_VCPKG) set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake CACHE STRING "Vcpkg toolchain file") endif () -project(sqlgen VERSION 0.3.0 LANGUAGES CXX) +project(sqlgen VERSION 0.4.0 LANGUAGES CXX) if (SQLGEN_BUILD_SHARED) add_library(sqlgen SHARED) @@ -52,10 +54,30 @@ else() add_library(sqlgen STATIC) endif() +add_library(sqlgen::sqlgen ALIAS sqlgen) + if (MSVC) - target_compile_options(sqlgen PRIVATE $<$:-Wall>) + target_compile_options(sqlgen PRIVATE + $<$: + -Wall + > + $<$: + -DNDEBUG + > + ) else() - target_compile_options(sqlgen PRIVATE $<$:-Wall -Wextra>) + target_compile_options(sqlgen PRIVATE + $<$: + -Wall -Wextra -Wpedantic -Wshadow -Wconversion + > + $<$: + -DNDEBUG + > + ) +endif() + +if (SQLGEN_BUILD_SHARED) + target_compile_definitions(sqlgen PUBLIC SQLGEN_BUILD_SHARED) endif() set(SQLGEN_SOURCES @@ -114,9 +136,11 @@ set_target_properties(sqlgen PROPERTIES LINKER_LANGUAGE CXX) target_sources(sqlgen PRIVATE ${SQLGEN_SOURCES}) if (SQLGEN_BUILD_TESTS) + add_library(sqlgen_tests_crt INTERFACE) + target_link_libraries(sqlgen_tests_crt INTERFACE sqlgen GTest::gtest_main) + enable_testing() find_package(GTest CONFIG REQUIRED) - set(SQLGEN_GTEST_LIB sqlgen GTest::gtest_main) add_subdirectory(tests) endif () diff --git a/include/sqlgen.hpp b/include/sqlgen.hpp index 8de6a00..c9faeeb 100644 --- a/include/sqlgen.hpp +++ b/include/sqlgen.hpp @@ -5,7 +5,6 @@ #include "sqlgen/Flatten.hpp" #include "sqlgen/ForeignKey.hpp" #include "sqlgen/Iterator.hpp" -#include "sqlgen/IteratorBase.hpp" #include "sqlgen/JSON.hpp" #include "sqlgen/Literal.hpp" #include "sqlgen/Pattern.hpp" @@ -44,6 +43,7 @@ #include "sqlgen/read.hpp" #include "sqlgen/rollback.hpp" #include "sqlgen/select_from.hpp" +#include "sqlgen/sqlgen_api.hpp" #include "sqlgen/to.hpp" #include "sqlgen/update.hpp" #include "sqlgen/where.hpp" diff --git a/include/sqlgen/Iterator.hpp b/include/sqlgen/Iterator.hpp index ba2e585..f3d4a9b 100644 --- a/include/sqlgen/Iterator.hpp +++ b/include/sqlgen/Iterator.hpp @@ -5,7 +5,6 @@ #include #include -#include "IteratorBase.hpp" #include "Ref.hpp" #include "Result.hpp" #include "internal/batch_size.hpp" @@ -15,23 +14,19 @@ namespace sqlgen { /// An input_iterator that returns the underlying type. -template +template class Iterator { public: using difference_type = std::ptrdiff_t; using value_type = Result; struct End { - bool operator==(const Iterator& _it) const noexcept { - return _it == *this; - } + bool operator==(const Iterator& _it) const noexcept { return _it == *this; } - bool operator!=(const Iterator& _it) const noexcept { - return _it != *this; - } + bool operator!=(const Iterator& _it) const noexcept { return _it != *this; } }; - Iterator(const Ref& _it) + Iterator(const Ref& _it) : current_batch_(get_next_batch(_it)), it_(_it), ix_(0) {} ~Iterator() = default; @@ -46,7 +41,7 @@ class Iterator { bool operator!=(const End& _end) const noexcept { return !(*this == _end); } - Iterator& operator++() noexcept { + Iterator& operator++() noexcept { ++ix_; if (ix_ >= current_batch_->size() && !it_->end()) { current_batch_ = get_next_batch(it_); @@ -59,7 +54,7 @@ class Iterator { private: static Ref>> get_next_batch( - const Ref& _it) noexcept { + const Ref& _it) noexcept { using namespace std::ranges::views; return _it->next(SQLGEN_BATCH_SIZE) .transform([](auto str_vec) { @@ -74,7 +69,7 @@ class Iterator { Ref>> current_batch_; /// The underlying database iterator. - Ref it_; + Ref it_; /// The current index in the current batch. size_t ix_; diff --git a/include/sqlgen/IteratorBase.hpp b/include/sqlgen/IteratorBase.hpp deleted file mode 100644 index 7bd3598..0000000 --- a/include/sqlgen/IteratorBase.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SQLGEN_ITERATORBASE_HPP_ -#define SQLGEN_ITERATORBASE_HPP_ - -#include -#include -#include - -#include "Result.hpp" - -namespace sqlgen { - -/// Abstract base class for an iterator to be returned by Connection::read(...). -struct IteratorBase { - virtual ~IteratorBase() = default; - - /// Whether the end of the available data has been reached. - virtual bool end() const = 0; - - /// Returns the next batch of rows. - /// If _batch_size is greater than the number of rows left, returns all - /// of the rows left. - virtual Result>>> next( - const size_t _batch_size) = 0; -}; - -} // namespace sqlgen - -#endif - diff --git a/include/sqlgen/Range.hpp b/include/sqlgen/Range.hpp index de7203d..b756bb0 100644 --- a/include/sqlgen/Range.hpp +++ b/include/sqlgen/Range.hpp @@ -4,7 +4,6 @@ #include #include -#include "Iterator.hpp" #include "Result.hpp" namespace sqlgen { diff --git a/include/sqlgen/Session.hpp b/include/sqlgen/Session.hpp index c469470..1d1375d 100644 --- a/include/sqlgen/Session.hpp +++ b/include/sqlgen/Session.hpp @@ -6,8 +6,8 @@ #include #include -#include "IteratorBase.hpp" #include "Ref.hpp" +#include "Result.hpp" #include "dynamic/Insert.hpp" #include "dynamic/SelectFrom.hpp" #include "dynamic/Statement.hpp" diff --git a/include/sqlgen/internal/iterator_t.hpp b/include/sqlgen/internal/iterator_t.hpp index 9e192f2..a2567bf 100644 --- a/include/sqlgen/internal/iterator_t.hpp +++ b/include/sqlgen/internal/iterator_t.hpp @@ -3,20 +3,29 @@ #include -#include "../Iterator.hpp" +#include "../Ref.hpp" +#include "../Result.hpp" namespace sqlgen::internal { template struct IteratorType; -/// Most database connectors can just use the standard iterator type, but -/// sometimes we need exception, in which case this template can be overridden. template -struct IteratorType { - using Type = Iterator; +struct IteratorType> { + using Type = + typename IteratorType>::Type; }; +template +struct IteratorType> { + using Type = + typename IteratorType>::Type; +}; + +// The specific iterator is implemented by each database in (database +// name)/Connection.hpp + template using iterator_t = typename IteratorType, std::remove_cvref_t>::Type; diff --git a/include/sqlgen/internal/strings/strings.hpp b/include/sqlgen/internal/strings/strings.hpp index 7dee0f0..aca410f 100644 --- a/include/sqlgen/internal/strings/strings.hpp +++ b/include/sqlgen/internal/strings/strings.hpp @@ -4,28 +4,33 @@ #include #include +#include "../../sqlgen_api.hpp" + namespace sqlgen::internal::strings { -char to_lower(const char ch); +SQLGEN_API char to_lower(const char ch); -std::string to_lower(const std::string& _str); +SQLGEN_API std::string to_lower(const std::string& _str); -char to_upper(const char ch); +SQLGEN_API char to_upper(const char ch); -std::string to_upper(const std::string& _str); +SQLGEN_API std::string to_upper(const std::string& _str); -std::string join(const std::string& _delimiter, - const std::vector& _strings); +SQLGEN_API std::string join(const std::string& _delimiter, + const std::vector& _strings); -std::string replace_all(const std::string& _str, const std::string& _from, - const std::string& _to); +SQLGEN_API std::string replace_all(const std::string& _str, + const std::string& _from, + const std::string& _to); -std::vector split(const std::string& _str, - const std::string& _delimiter); +SQLGEN_API std::vector split(const std::string& _str, + const std::string& _delimiter); -std::string ltrim(const std::string& _str, const std::string& _chars = " "); +SQLGEN_API std::string ltrim(const std::string& _str, + const std::string& _chars = " "); -std::string rtrim(const std::string& _str, const std::string& _chars = " "); +SQLGEN_API std::string rtrim(const std::string& _str, + const std::string& _chars = " "); inline std::string trim(const std::string& _str, const std::string& _chars = " ") { diff --git a/include/sqlgen/is_connection.hpp b/include/sqlgen/is_connection.hpp index 29f689e..e526a7f 100644 --- a/include/sqlgen/is_connection.hpp +++ b/include/sqlgen/is_connection.hpp @@ -6,7 +6,6 @@ #include #include -#include "IteratorBase.hpp" #include "Ref.hpp" #include "Result.hpp" #include "dynamic/SelectFrom.hpp" diff --git a/include/sqlgen/mysql/Connection.hpp b/include/sqlgen/mysql/Connection.hpp index f5894c7..760edf9 100644 --- a/include/sqlgen/mysql/Connection.hpp +++ b/include/sqlgen/mysql/Connection.hpp @@ -9,7 +9,6 @@ #include #include "../Iterator.hpp" -#include "../IteratorBase.hpp" #include "../Ref.hpp" #include "../Result.hpp" #include "../Transaction.hpp" @@ -19,35 +18,32 @@ #include "../internal/to_container.hpp" #include "../internal/write_or_insert.hpp" #include "../is_connection.hpp" +#include "../sqlgen_api.hpp" #include "../transpilation/value_t.hpp" #include "Credentials.hpp" +#include "Iterator.hpp" #include "exec.hpp" #include "to_sql.hpp" namespace sqlgen::mysql { -class Connection { +class SQLGEN_API Connection { using ConnPtr = Ref; using StmtPtr = std::shared_ptr; public: - Connection(const Credentials& _credentials) - : conn_(make_conn(_credentials)) {} + Connection(const Credentials& _credentials); static rfl::Result> make( const Credentials& _credentials) noexcept; - ~Connection() = default; + ~Connection(); - Result begin_transaction() noexcept { - return execute("START TRANSACTION;"); - } + Result begin_transaction() noexcept; - Result commit() noexcept { return execute("COMMIT;"); } + Result commit() noexcept; - Result execute(const std::string& _sql) noexcept { - return exec(conn_, _sql); - } + Result execute(const std::string& _sql) noexcept; template Result insert(const dynamic::Insert& _stmt, ItBegin _begin, @@ -60,15 +56,15 @@ class Connection { template auto read(const dynamic::SelectFrom& _query) { using ValueType = transpilation::value_t; - return internal::to_container(read_impl(_query).transform( - [](auto&& _it) { return Iterator(std::move(_it)); })); + return internal::to_container( + read_impl(_query).transform([](auto&& _it) { + return sqlgen::Iterator(std::move(_it)); + })); } - Result rollback() noexcept { return execute("ROLLBACK;"); } + Result rollback() noexcept; - std::string to_sql(const dynamic::Statement& _stmt) noexcept { - return to_sql_impl(_stmt); - } + std::string to_sql(const dynamic::Statement& _stmt) noexcept; Result start_write(const dynamic::Write& _stmt); @@ -98,7 +94,7 @@ class Connection { const std::variant& _stmt) const noexcept; - Result> read_impl(const dynamic::SelectFrom& _query); + Result> read_impl(const dynamic::SelectFrom& _query); Result write_impl( const std::vector>>& _data); @@ -119,4 +115,13 @@ static_assert(is_connection>, } // namespace sqlgen::mysql +namespace sqlgen::internal { + +template +struct IteratorType { + using Type = Iterator; +}; + +} // namespace sqlgen::internal + #endif diff --git a/include/sqlgen/mysql/Iterator.hpp b/include/sqlgen/mysql/Iterator.hpp index 3f85a54..0f9259f 100644 --- a/include/sqlgen/mysql/Iterator.hpp +++ b/include/sqlgen/mysql/Iterator.hpp @@ -8,14 +8,13 @@ #include #include -#include "../IteratorBase.hpp" #include "../Ref.hpp" #include "../Result.hpp" -#include "Connection.hpp" +#include "../sqlgen_api.hpp" namespace sqlgen::mysql { -class Iterator : public sqlgen::IteratorBase { +class SQLGEN_API Iterator { using ConnPtr = Ref; using ResPtr = Ref; @@ -25,13 +24,13 @@ class Iterator : public sqlgen::IteratorBase { ~Iterator(); /// Whether the end of the available data has been reached. - bool end() const final { return end_; } + bool end() const; /// Returns the next batch of rows. /// If _batch_size is greater than the number of rows left, returns all /// of the rows left. Result>>> next( - const size_t _batch_size) final; + const size_t _batch_size); private: /// The underlying mysql result. diff --git a/include/sqlgen/mysql/exec.hpp b/include/sqlgen/mysql/exec.hpp index 3a4f030..b16706a 100644 --- a/include/sqlgen/mysql/exec.hpp +++ b/include/sqlgen/mysql/exec.hpp @@ -7,10 +7,12 @@ #include "../Ref.hpp" #include "../Result.hpp" +#include "../sqlgen_api.hpp" namespace sqlgen::mysql { -Result exec(const Ref& _conn, const std::string& _sql) noexcept; +Result SQLGEN_API exec(const Ref& _conn, + const std::string& _sql) noexcept; } // namespace sqlgen::mysql diff --git a/include/sqlgen/mysql/to_sql.hpp b/include/sqlgen/mysql/to_sql.hpp index 02f86aa..c2f523e 100644 --- a/include/sqlgen/mysql/to_sql.hpp +++ b/include/sqlgen/mysql/to_sql.hpp @@ -5,12 +5,13 @@ #include #include "../dynamic/Statement.hpp" +#include "../sqlgen_api.hpp" #include "../transpilation/to_sql.hpp" namespace sqlgen::mysql { /// Transpiles a dynamic general SQL statement to the mysql dialect. -std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept; +std::string SQLGEN_API to_sql_impl(const dynamic::Statement& _stmt) noexcept; /// Transpiles any SQL statement to the mysql dialect. template diff --git a/include/sqlgen/postgres/Connection.hpp b/include/sqlgen/postgres/Connection.hpp index 8cb7494..af50b10 100644 --- a/include/sqlgen/postgres/Connection.hpp +++ b/include/sqlgen/postgres/Connection.hpp @@ -8,7 +8,7 @@ #include #include -#include "../IteratorBase.hpp" +#include "../Iterator.hpp" #include "../Ref.hpp" #include "../Result.hpp" #include "../Transaction.hpp" @@ -18,32 +18,31 @@ #include "../internal/to_container.hpp" #include "../internal/write_or_insert.hpp" #include "../is_connection.hpp" +#include "../sqlgen_api.hpp" #include "../transpilation/value_t.hpp" #include "Credentials.hpp" +#include "Iterator.hpp" #include "exec.hpp" #include "to_sql.hpp" namespace sqlgen::postgres { -class Connection { +class SQLGEN_API Connection { using ConnPtr = Ref; public: - Connection(const Credentials& _credentials) - : conn_(make_conn(_credentials.to_str())), credentials_(_credentials) {} + Connection(const Credentials& _credentials); static rfl::Result> make( const Credentials& _credentials) noexcept; - ~Connection() = default; + ~Connection(); Result begin_transaction() noexcept; Result commit() noexcept; - Result execute(const std::string& _sql) noexcept { - return exec(conn_, _sql).transform([](auto&&) { return Nothing{}; }); - } + Result execute(const std::string& _sql) noexcept; template Result insert(const dynamic::Insert& _stmt, ItBegin _begin, @@ -56,19 +55,18 @@ class Connection { template auto read(const dynamic::SelectFrom& _query) { using ValueType = transpilation::value_t; - return internal::to_container(read_impl(_query).transform( - [](auto&& _it) { return Iterator(std::move(_it)); })); + return internal::to_container( + read_impl(_query).transform([](auto&& _it) { + return sqlgen::Iterator( + std::move(_it)); + })); } Result rollback() noexcept; - std::string to_sql(const dynamic::Statement& _stmt) noexcept { - return postgres::to_sql_impl(_stmt); - } + std::string to_sql(const dynamic::Statement& _stmt) noexcept; - Result start_write(const dynamic::Write& _stmt) { - return execute(postgres::to_sql_impl(_stmt)); - } + Result start_write(const dynamic::Write& _stmt); Result end_write(); @@ -86,7 +84,7 @@ class Connection { static ConnPtr make_conn(const std::string& _conn_str); - Result> read_impl(const dynamic::SelectFrom& _query); + Result> read_impl(const dynamic::SelectFrom& _query); std::string to_buffer( const std::vector>& _line) const noexcept; @@ -107,4 +105,13 @@ static_assert(is_connection>, } // namespace sqlgen::postgres +namespace sqlgen::internal { + +template +struct IteratorType { + using Type = Iterator; +}; + +} // namespace sqlgen::internal + #endif diff --git a/include/sqlgen/postgres/Iterator.hpp b/include/sqlgen/postgres/Iterator.hpp index d512e53..f43d1e2 100644 --- a/include/sqlgen/postgres/Iterator.hpp +++ b/include/sqlgen/postgres/Iterator.hpp @@ -8,14 +8,13 @@ #include #include -#include "../IteratorBase.hpp" #include "../Ref.hpp" #include "../Result.hpp" -#include "Connection.hpp" +#include "../sqlgen_api.hpp" namespace sqlgen::postgres { -class Iterator : public sqlgen::IteratorBase { +class SQLGEN_API Iterator { using ConnPtr = Ref; public: @@ -28,13 +27,13 @@ class Iterator : public sqlgen::IteratorBase { ~Iterator(); /// Whether the end of the available data has been reached. - bool end() const final; + bool end() const; /// Returns the next batch of rows. /// If _batch_size is greater than the number of rows left, returns all /// of the rows left. Result>>> next( - const size_t _batch_size) final; + const size_t _batch_size); Iterator& operator=(const Iterator& _other) = delete; diff --git a/include/sqlgen/postgres/exec.hpp b/include/sqlgen/postgres/exec.hpp index d6a81ea..57ef72f 100644 --- a/include/sqlgen/postgres/exec.hpp +++ b/include/sqlgen/postgres/exec.hpp @@ -8,11 +8,12 @@ #include "../Ref.hpp" #include "../Result.hpp" +#include "../sqlgen_api.hpp" namespace sqlgen::postgres { -Result> exec(const Ref& _conn, - const std::string& _sql) noexcept; +Result> SQLGEN_API exec(const Ref& _conn, + const std::string& _sql) noexcept; } // namespace sqlgen::postgres diff --git a/include/sqlgen/postgres/to_sql.hpp b/include/sqlgen/postgres/to_sql.hpp index da663b4..44195a8 100644 --- a/include/sqlgen/postgres/to_sql.hpp +++ b/include/sqlgen/postgres/to_sql.hpp @@ -5,12 +5,13 @@ #include #include "../dynamic/Statement.hpp" +#include "../sqlgen_api.hpp" #include "../transpilation/to_sql.hpp" namespace sqlgen::postgres { /// Transpiles a dynamic general SQL statement to the postgres dialect. -std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept; +std::string SQLGEN_API to_sql_impl(const dynamic::Statement& _stmt) noexcept; /// Transpiles any SQL statement to the postgres dialect. template diff --git a/include/sqlgen/sqlgen_api.hpp b/include/sqlgen/sqlgen_api.hpp new file mode 100644 index 0000000..d53cf7a --- /dev/null +++ b/include/sqlgen/sqlgen_api.hpp @@ -0,0 +1,14 @@ +#ifndef SQLGEN_SQLGEN_API_HPP_ +#define SQLGEN_SQLGEN_API_HPP_ + +#ifdef SQLGEN_BUILD_SHARED +#ifdef _WIN32 +#define SQLGEN_API __declspec(dllexport) +#else +#define SQLGEN_API __attribute__((visibility("default"))) +#endif +#else +#define SQLGEN_API +#endif + +#endif diff --git a/include/sqlgen/sqlite/Connection.hpp b/include/sqlgen/sqlite/Connection.hpp index 7d68702..bc34404 100644 --- a/include/sqlgen/sqlite/Connection.hpp +++ b/include/sqlgen/sqlite/Connection.hpp @@ -9,7 +9,7 @@ #include #include -#include "../IteratorBase.hpp" +#include "../Iterator.hpp" #include "../Ref.hpp" #include "../Result.hpp" #include "../Transaction.hpp" @@ -17,22 +17,23 @@ #include "../internal/to_container.hpp" #include "../internal/write_or_insert.hpp" #include "../is_connection.hpp" +#include "../sqlgen_api.hpp" #include "../transpilation/value_t.hpp" +#include "Iterator.hpp" #include "to_sql.hpp" namespace sqlgen::sqlite { -class Connection { +class SQLGEN_API Connection { using ConnPtr = Ref; using StmtPtr = std::shared_ptr; public: - Connection(const std::string& _fname) - : stmt_(nullptr), conn_(make_conn(_fname)) {} + Connection(const std::string& _fname); static rfl::Result> make(const std::string& _fname) noexcept; - ~Connection() = default; + ~Connection(); Result begin_transaction() noexcept; @@ -51,15 +52,15 @@ class Connection { template auto read(const dynamic::SelectFrom& _query) { using ValueType = transpilation::value_t; - return internal::to_container(read_impl(_query).transform( - [](auto&& _it) { return Iterator(std::move(_it)); })); + return internal::to_container( + read_impl(_query).transform([](auto&& _it) { + return sqlgen::Iterator(std::move(_it)); + })); } Result rollback() noexcept; - std::string to_sql(const dynamic::Statement& _stmt) noexcept { - return sqlite::to_sql_impl(_stmt); - } + std::string to_sql(const dynamic::Statement& _stmt) noexcept; Result start_write(const dynamic::Write& _stmt); @@ -91,7 +92,7 @@ class Connection { Result prepare_statement(const std::string& _sql) const noexcept; /// Implements the actual read. - Result> read_impl(const dynamic::SelectFrom& _query); + Result> read_impl(const dynamic::SelectFrom& _query); /// Implements the actual write Result write_impl( @@ -113,4 +114,13 @@ static_assert(is_connection>, } // namespace sqlgen::sqlite +namespace sqlgen::internal { + +template +struct IteratorType { + using Type = Iterator; +}; + +} // namespace sqlgen::internal + #endif diff --git a/include/sqlgen/sqlite/Iterator.hpp b/include/sqlgen/sqlite/Iterator.hpp index 985d63e..619fbd9 100644 --- a/include/sqlgen/sqlite/Iterator.hpp +++ b/include/sqlgen/sqlite/Iterator.hpp @@ -7,14 +7,13 @@ #include #include -#include "../IteratorBase.hpp" #include "../Ref.hpp" #include "../Result.hpp" -#include "Connection.hpp" +#include "../sqlgen_api.hpp" namespace sqlgen::sqlite { -class Iterator : public sqlgen::IteratorBase { +class SQLGEN_API Iterator { using ConnPtr = Ref; using StmtPtr = Ref; @@ -24,13 +23,13 @@ class Iterator : public sqlgen::IteratorBase { ~Iterator(); /// Whether the end of the available data has been reached. - bool end() const final; + bool end() const; /// Returns the next batch of rows. /// If _batch_size is greater than the number of rows left, returns all /// of the rows left. Result>>> next( - const size_t _batch_size) final; + const size_t _batch_size); private: void step() { end_ = (sqlite3_step(stmt_.get()) != SQLITE_ROW); } diff --git a/include/sqlgen/sqlite/to_sql.hpp b/include/sqlgen/sqlite/to_sql.hpp index 9959e30..6f58e5e 100644 --- a/include/sqlgen/sqlite/to_sql.hpp +++ b/include/sqlgen/sqlite/to_sql.hpp @@ -4,12 +4,13 @@ #include #include "../dynamic/Statement.hpp" +#include "../sqlgen_api.hpp" #include "../transpilation/to_sql.hpp" namespace sqlgen::sqlite { /// Transpiles a dynamic general SQL statement to the sqlite dialect. -std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept; +std::string SQLGEN_API to_sql_impl(const dynamic::Statement& _stmt) noexcept; /// Transpiles any SQL statement to the sqlite dialect. template diff --git a/src/sqlgen/mysql/Connection.cpp b/src/sqlgen/mysql/Connection.cpp index f7b36b0..2e1d407 100644 --- a/src/sqlgen/mysql/Connection.cpp +++ b/src/sqlgen/mysql/Connection.cpp @@ -14,6 +14,11 @@ namespace sqlgen::mysql { +Connection::Connection(const Credentials& _credentials) + : conn_(make_conn(_credentials)) {} + +Connection::~Connection() = default; + Result Connection::actual_insert( const std::vector>>& _data, MYSQL_STMT* _stmt) const noexcept { @@ -70,6 +75,16 @@ Result Connection::actual_insert( return Nothing{}; } +Result Connection::begin_transaction() noexcept { + return execute("START TRANSACTION;"); +} + +Result Connection::commit() noexcept { return execute("COMMIT;"); } + +Result Connection::execute(const std::string& _sql) noexcept { + return exec(conn_, _sql); +} + Result Connection::insert_impl( const dynamic::Insert& _stmt, const std::vector>>& @@ -122,8 +137,7 @@ Result Connection::prepare_statement( return stmt_ptr; } -Result> Connection::read_impl( - const dynamic::SelectFrom& _query) { +Result> Connection::read_impl(const dynamic::SelectFrom& _query) { const auto sql = mysql::to_sql_impl(_query); const auto err = mysql_real_query(conn_.get(), sql.c_str(), static_cast(sql.size())); @@ -139,6 +153,8 @@ Result> Connection::read_impl( .transform([&](auto&& _res) { return Ref::make(_res, conn_); }); } +Result Connection::rollback() noexcept { return execute("ROLLBACK;"); } + Result Connection::start_write(const dynamic::Write& _write_stmt) { if (stmt_) { return error( @@ -157,6 +173,10 @@ Result Connection::start_write(const dynamic::Write& _write_stmt) { }); } +std::string Connection::to_sql(const dynamic::Statement& _stmt) noexcept { + return to_sql_impl(_stmt); +} + Result Connection::write_impl( const std::vector>>& _data) { if (!stmt_) { diff --git a/src/sqlgen/mysql/Iterator.cpp b/src/sqlgen/mysql/Iterator.cpp index a26bf11..7839c3a 100644 --- a/src/sqlgen/mysql/Iterator.cpp +++ b/src/sqlgen/mysql/Iterator.cpp @@ -7,6 +7,8 @@ Iterator::Iterator(const ResPtr& _res, const ConnPtr& _conn) Iterator::~Iterator() = default; +bool Iterator::end() const { return end_; } + Result>>> Iterator::next( const size_t _batch_size) { std::vector>> vec; diff --git a/src/sqlgen/postgres/Connection.cpp b/src/sqlgen/postgres/Connection.cpp index bd6dba6..6b33ad2 100644 --- a/src/sqlgen/postgres/Connection.cpp +++ b/src/sqlgen/postgres/Connection.cpp @@ -12,12 +12,21 @@ namespace sqlgen::postgres { +Connection::Connection(const Credentials& _credentials) + : conn_(make_conn(_credentials.to_str())), credentials_(_credentials) {} + +Connection::~Connection() = default; + Result Connection::begin_transaction() noexcept { return execute("BEGIN TRANSACTION;"); } Result Connection::commit() noexcept { return execute("COMMIT;"); } +Result Connection::execute(const std::string& _sql) noexcept { + return exec(conn_, _sql).transform([](auto&&) { return Nothing{}; }); +} + Result Connection::end_write() { if (PQputCopyEnd(conn_.get(), NULL) == -1) { return error(PQerrorMessage(conn_.get())); @@ -114,11 +123,10 @@ typename Connection::ConnPtr Connection::make_conn( return ConnPtr::make(std::shared_ptr(raw_ptr, &PQfinish)).value(); } -Result> Connection::read_impl( - const dynamic::SelectFrom& _query) { +Result> Connection::read_impl(const dynamic::SelectFrom& _query) { const auto sql = postgres::to_sql_impl(_query); try { - return Ref(Ref::make(sql, conn_)); + return Ref::make(sql, conn_); } catch (std::exception& e) { return error(e.what()); } @@ -146,6 +154,14 @@ std::string Connection::to_buffer( "\n"; } +std::string Connection::to_sql(const dynamic::Statement& _stmt) noexcept { + return postgres::to_sql_impl(_stmt); +} + +Result Connection::start_write(const dynamic::Write& _stmt) { + return execute(postgres::to_sql_impl(_stmt)); +} + Result Connection::write_impl( const std::vector>>& _data) { for (const auto& line : _data) { diff --git a/src/sqlgen/sqlite/Connection.cpp b/src/sqlgen/sqlite/Connection.cpp index b5d3cdf..0aa44d9 100644 --- a/src/sqlgen/sqlite/Connection.cpp +++ b/src/sqlgen/sqlite/Connection.cpp @@ -6,11 +6,15 @@ #include "sqlgen/internal/collect/vector.hpp" #include "sqlgen/internal/strings/strings.hpp" -#include "sqlgen/sqlite/Iterator.hpp" #include "sqlgen/sqlite/to_sql.hpp" namespace sqlgen::sqlite { +Connection::Connection(const std::string& _fname) + : stmt_(nullptr), conn_(make_conn(_fname)) {} + +Connection::~Connection() = default; + Result Connection::actual_insert( const std::vector>>& _data, sqlite3_stmt* _stmt) const noexcept { @@ -97,8 +101,7 @@ typename Connection::ConnPtr Connection::make_conn(const std::string& _fname) { return ConnPtr::make(std::shared_ptr(conn, &sqlite3_close)).value(); } -Result> Connection::read_impl( - const dynamic::SelectFrom& _query) { +Result> Connection::read_impl(const dynamic::SelectFrom& _query) { const auto sql = to_sql_impl(_query); sqlite3_stmt* p_stmt = nullptr; @@ -115,9 +118,7 @@ Result> Connection::read_impl( } return Ref::make(StmtPtr(p_stmt, &sqlite3_finalize)) - .transform([&](auto _stmt) -> Ref { - return Ref::make(_stmt, conn_); - }); + .transform([&](auto _stmt) { return Ref::make(_stmt, conn_); }); } Result Connection::prepare_statement( @@ -141,6 +142,10 @@ Result Connection::prepare_statement( Result Connection::rollback() noexcept { return execute("ROLLBACK;"); } +std::string Connection::to_sql(const dynamic::Statement& _stmt) noexcept { + return sqlite::to_sql_impl(_stmt); +} + Result Connection::start_write(const dynamic::Write& _stmt) { if (stmt_) { return error( diff --git a/tests/mysql/CMakeLists.txt b/tests/mysql/CMakeLists.txt index c00824f..47396a1 100644 --- a/tests/mysql/CMakeLists.txt +++ b/tests/mysql/CMakeLists.txt @@ -8,11 +8,11 @@ add_executable( ) target_precompile_headers(sqlgen-mysql-tests PRIVATE [["sqlgen.hpp"]] ) +target_link_libraries(sqlgen-mysql-tests PRIVATE sqlgen_tests_crt) -target_link_libraries( - sqlgen-mysql-tests - PRIVATE - "${SQLGEN_GTEST_LIB}" +add_custom_command(TARGET sqlgen-mysql-tests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy -t $ $ + COMMAND_EXPAND_LISTS ) find_package(GTest) diff --git a/tests/postgres/CMakeLists.txt b/tests/postgres/CMakeLists.txt index 18f8296..2fa3582 100644 --- a/tests/postgres/CMakeLists.txt +++ b/tests/postgres/CMakeLists.txt @@ -8,11 +8,11 @@ add_executable( ) target_precompile_headers(sqlgen-postgres-tests PRIVATE [["sqlgen.hpp"]] ) +target_link_libraries(sqlgen-postgres-tests PRIVATE sqlgen_tests_crt) -target_link_libraries( - sqlgen-postgres-tests - PRIVATE - "${SQLGEN_GTEST_LIB}" +add_custom_command(TARGET sqlgen-postgres-tests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy -t $ $ + COMMAND_EXPAND_LISTS ) find_package(GTest) diff --git a/tests/sqlite/CMakeLists.txt b/tests/sqlite/CMakeLists.txt index a744712..d0a2e4d 100644 --- a/tests/sqlite/CMakeLists.txt +++ b/tests/sqlite/CMakeLists.txt @@ -8,11 +8,11 @@ add_executable( ) target_precompile_headers(sqlgen-sqlite-tests PRIVATE [["sqlgen.hpp"]] ) +target_link_libraries(sqlgen-sqlite-tests PRIVATE sqlgen_tests_crt) -target_link_libraries( - sqlgen-sqlite-tests - PRIVATE - "${SQLGEN_GTEST_LIB}" +add_custom_command(TARGET sqlgen-sqlite-tests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy -t $ $ + COMMAND_EXPAND_LISTS ) find_package(GTest)