diff options
author | Vittorio Romeo <vittorio.romeo@outlook.com> | 2021-11-12 18:58:09 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-12 18:58:09 +0000 |
commit | ce42f7ebcdeac94f13214b3cb725806c41cccdee (patch) | |
tree | 14e9967891942ccc45ea6cc2f31cc53d17a062f3 | |
parent | 13f8c1bb2f6f39cbddf0821536b3c7537d9f9bf9 (diff) | |
parent | f5acb339115bbf3d6c14c24d190d82189c1bbfcb (diff) |
Merge pull request #361 from SuperV1234/2.1.2
Open Hexagon v2.1.2
27 files changed, 6742 insertions, 2457 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index b2f42b65..89322263 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -317,11 +317,18 @@ vrm_cmake_include_vc_dependency_once(vc_detection) # Targets # ----------------------------------------------------------------------------- +if(WIN32 AND "${vrm_cmake_build_type_lower}" STREQUAL "release") + set(SSVOH_BUILD_WIN32_CONSOLE TRUE) +else() + set(SSVOH_BUILD_WIN32_CONSOLE FALSE) +endif() + add_library(SSVOpenHexagonLibC STATIC ${C_SRC_LIST}) add_library(SSVOpenHexagonLib STATIC ${SRC_LIST}) -if(WIN32 AND "${vrm_cmake_build_type_lower}" STREQUAL "release") +if(SSVOH_BUILD_WIN32_CONSOLE) add_executable(SSVOpenHexagon WIN32 ${MAIN_FILE}) + add_executable(SSVOpenHexagon-Console ${MAIN_FILE}) else() add_executable(SSVOpenHexagon ${MAIN_FILE}) endif() @@ -339,6 +346,10 @@ target_precompile_headers( target_precompile_headers(SSVOpenHexagon REUSE_FROM SSVOpenHexagonLib) +if(SSVOH_BUILD_WIN32_CONSOLE) + target_precompile_headers(SSVOpenHexagon-Console REUSE_FROM SSVOpenHexagonLib) +endif() + # # # ----------------------------------------------------------------------------- @@ -405,20 +416,29 @@ ssvoh_find_extlib_for_target(SSVOpenHexagonLib SSVStart) target_link_libraries(SSVOpenHexagon SSVOpenHexagonLib SSVOpenHexagonLibC) -if(WIN32 AND "${vrm_cmake_build_type_lower}" STREQUAL "release") +if(SSVOH_BUILD_WIN32_CONSOLE) target_link_libraries(SSVOpenHexagon "${CMAKE_SOURCE_DIR}/art/icon.res" sfml-main) target_compile_options(SSVOpenHexagon PRIVATE "-Wl,-subsystem,windows") + target_link_libraries(SSVOpenHexagon-Console SSVOpenHexagonLib SSVOpenHexagonLibC) endif() +set(SSVOH_INCLUDE_DIRECTORIES ${SFML_SOURCE_DIR}/include + ${PUBLIC_INCLUDE_DIRS} + ${zlib_SOURCE_DIR} + ${zlib_BINARY_DIR} + ${LUASRC} + ${imgui_SOURCE_DIR}) + target_include_directories( - SSVOpenHexagon SYSTEM PUBLIC ${SFML_SOURCE_DIR}/include - PUBLIC ${PUBLIC_INCLUDE_DIRS} - PUBLIC ${zlib_SOURCE_DIR} - PUBLIC ${zlib_BINARY_DIR} - PUBLIC ${LUASRC} - PUBLIC ${imgui_SOURCE_DIR} + SSVOpenHexagon SYSTEM PUBLIC ${SSVOH_INCLUDE_DIRECTORIES} ) +if(SSVOH_BUILD_WIN32_CONSOLE) + target_include_directories( + SSVOpenHexagon-Console SYSTEM PUBLIC ${SSVOH_INCLUDE_DIRECTORIES} + ) +endif() + if(UNIX AND NOT APPLE) target_link_libraries(SSVOpenHexagonLib pthread) endif() @@ -427,6 +447,13 @@ install( TARGETS SSVOpenHexagon RUNTIME DESTINATION ${CMAKE_SOURCE_DIR}/_RELEASE/ ) +if(SSVOH_BUILD_WIN32_CONSOLE) + install( + TARGETS SSVOpenHexagon-Console RUNTIME DESTINATION ${CMAKE_SOURCE_DIR}/_RELEASE/ + ) +endif() + + # # # ----------------------------------------------------------------------------- diff --git a/_RELEASE/Assets/assets.json b/_RELEASE/Assets/assets.json index 180c13b8..72785956 100644 --- a/_RELEASE/Assets/assets.json +++ b/_RELEASE/Assets/assets.json @@ -20,6 +20,7 @@ "onlineIcon.png", "onlineIconFail.png", "replayIcon.png", + "smallCircle.png", "starParticle.png", "titleBar.png" ], diff --git a/_RELEASE/Assets/smallCircle.png b/_RELEASE/Assets/smallCircle.png Binary files differnew file mode 100644 index 00000000..7bb504f7 --- /dev/null +++ b/_RELEASE/Assets/smallCircle.png diff --git a/_RELEASE/Packs/tutorial/Scripts/Levels/babysteps.lua b/_RELEASE/Packs/tutorial/Scripts/Levels/babysteps.lua index f56e51ef..a7bd3ea7 100644 --- a/_RELEASE/Packs/tutorial/Scripts/Levels/babysteps.lua +++ b/_RELEASE/Packs/tutorial/Scripts/Levels/babysteps.lua @@ -172,7 +172,7 @@ function onUpdate(mFrameTime) step35 = true l_clearTracked() - l_addTracked("challengeFailedText", "surived until the end") + l_addTracked("challengeFailedText", "survived until the end") l_resetTime() l_setIncTime(15) diff --git a/_RELEASE/config.json b/_RELEASE/config.json index 0a2c6165..eb793cac 100644 --- a/_RELEASE/config.json +++ b/_RELEASE/config.json @@ -3,11 +3,13 @@ "3D_enabled" : true,
"3D_max_depth" : 100,
"3D_multiplier" : 1.0,
+ "angle_tilt_intensity" : 1.0,
"antialiasing_level" : 16,
"auto_restart" : false,
"auto_zoom_factor" : true,
"beatpulse_enabled" : true,
"black_and_white" : false,
+ "camera_shake_multiplier" : 1.000002622604370,
"darken_uneven_background_chunk" : true,
"debug" : true,
"draw_text_outlines" : true,
@@ -48,12 +50,15 @@ "player_focus_speed" : 4.6250,
"player_size" : 7.300000190734863,
"player_speed" : 9.449999809265137,
+ "player_trail_alpha" : 255,
+ "player_trail_decay" : 0.50,
+ "player_trail_scale" : 1.0,
"pulse_enabled" : true,
"rotate_to_start" : false,
"save_last_login_username" : true,
"save_local_best_replay_to_file" : true,
"server_control_port" : 50506,
- "server_ip" : "127.0.0.1",
+ "server_ip" : "139.162.199.162",
"server_level_whitelist" :
[
"ohvrvanilla_vittorio_romeo_cube_1_apeirogon_m_0.35",
@@ -129,6 +134,7 @@ "show_level_info" : true,
"show_login_at_startup" : false,
"show_messages" : true,
+ "show_player_trail" : true,
"show_status_text" : true,
"show_timer" : true,
"show_tracked_variables" : true,
diff --git a/art/eventcover.png b/art/eventcover.png Binary files differindex e15e1426..7835aaf9 100644 --- a/art/eventcover.png +++ b/art/eventcover.png diff --git a/art/eventcover.psd b/art/eventcover.psd Binary files differindex c72a3e56..e933605d 100644 --- a/art/eventcover.psd +++ b/art/eventcover.psd diff --git a/build/make_debug_client_win10_msys_0_cmake.sh b/build/make_debug_client_win10_msys_0_cmake.sh index b63dab6c..5641acb6 100644 --- a/build/make_debug_client_win10_msys_0_cmake.sh +++ b/build/make_debug_client_win10_msys_0_cmake.sh @@ -17,7 +17,7 @@ cmake .. -G"Ninja" \ -DCMAKE_CXX_COMPILER="g++" \ -DCMAKE_CXX_FLAGS="\ -fuse-ld=lld \ - -O0 -fno-omit-frame-pointer \ + -Og -g3 -fno-omit-frame-pointer \ -Wall -Wextra -Wpedantic -Wno-braced-scalar-init -Wno-missing-field-initializers \ -D_GLIBCXX_ASSERTIONS=1 -D_FORTIFY_SOURCE=2 \ -fstack-protector -Wno-pragmas\ diff --git a/buildrel/make_release_client_win10_msys_2_copy.sh b/buildrel/make_release_client_win10_msys_2_copy.sh index b63d450c..c29110a7 100644 --- a/buildrel/make_release_client_win10_msys_2_copy.sh +++ b/buildrel/make_release_client_win10_msys_2_copy.sh @@ -10,6 +10,7 @@ echo "--------------------------------------------------------------------" echo "" cp ./SSVOpenHexagon.exe ../_RELEASE +cp ./SSVOpenHexagon-Console.exe ../_RELEASE cp ./OHWorkshopUploader.exe ../_RELEASE cp ./OHServerControl.exe ../_RELEASE diff --git a/include/SSVOpenHexagon/Components/CPlayer.hpp b/include/SSVOpenHexagon/Components/CPlayer.hpp index 2ff20ad7..4e008e06 100644 --- a/include/SSVOpenHexagon/Components/CPlayer.hpp +++ b/include/SSVOpenHexagon/Components/CPlayer.hpp @@ -64,6 +64,8 @@ private: Ticker _swapBlinkTimer; Ticker _deadEffectTimer; + float _currTiltedAngle; + void drawPivot(const unsigned int sides, const sf::Color& colorMain, Utils::FastVertexVectorQuads& wallQuads, Utils::FastVertexVectorTris& capTris, const sf::Color& capColor); @@ -112,10 +114,14 @@ public: void updatePosition(const float radius); + [[nodiscard]] sf::Color getColorAdjustedForSwap( + const sf::Color& colorPlayer) const; + void draw(const unsigned int sides, const sf::Color& colorMain, const sf::Color& colorPlayer, Utils::FastVertexVectorQuads& wallQuads, Utils::FastVertexVectorTris& capTris, - Utils::FastVertexVectorTris& playerTris, const sf::Color& capColor); + Utils::FastVertexVectorTris& playerTris, const sf::Color& capColor, + const float angleTiltIntensity); [[nodiscard]] bool push(const int movementDir, const float radius, const CWall& wall, const sf::Vector2f& mCenterPos, @@ -126,10 +132,15 @@ public: [[nodiscard]] bool getJustSwapped() const noexcept; - [[nodiscard]] bool isReadyToSwap() const noexcept + [[nodiscard, gnu::always_inline]] bool isReadyToSwap() const noexcept { return !_swapTimer.isRunning(); } + + [[nodiscard, gnu::always_inline]] bool hasChangedAngle() const noexcept + { + return _angle != _lastAngle; + } }; } // namespace hg diff --git a/include/SSVOpenHexagon/Core/HGStatus.hpp b/include/SSVOpenHexagon/Core/HGStatus.hpp index 06e79a63..b5c54f0c 100644 --- a/include/SSVOpenHexagon/Core/HGStatus.hpp +++ b/include/SSVOpenHexagon/Core/HGStatus.hpp @@ -58,6 +58,7 @@ public: bool started{false}; std::string restartInput; std::string replayInput; + bool showPlayerTrail{true}; // Reset all the time points and signal that we started void start() noexcept; diff --git a/include/SSVOpenHexagon/Core/HexagonGame.hpp b/include/SSVOpenHexagon/Core/HexagonGame.hpp index 55909b18..265acfb4 100644 --- a/include/SSVOpenHexagon/Core/HexagonGame.hpp +++ b/include/SSVOpenHexagon/Core/HexagonGame.hpp @@ -174,7 +174,17 @@ private: float angularVelocity; }; + struct TrailParticle + { + sf::Sprite sprite; + float angle; + }; + + sf::Texture* txStarParticle; + sf::Texture* txSmallCircle; + std::vector<Particle> particles; + std::vector<TrailParticle> trailParticles; bool mustSpawnPBParticles{false}; float nextPBParticleSpawn{0.f}; float pbTextGrowth{0.f}; @@ -333,6 +343,7 @@ private: void updateKeyIcons(); void updateLevelInfo(); void updateParticles(ssvu::FT mFT); + void updateTrailParticles(ssvu::FT mFT); // Post update methods void postUpdate(); @@ -353,6 +364,7 @@ private: void drawKeyIcons(); void drawLevelInfo(); void drawParticles(); + void drawTrailParticles(); void drawImguiLuaConsole(); // Data-related methods diff --git a/include/SSVOpenHexagon/Global/Config.hpp b/include/SSVOpenHexagon/Global/Config.hpp index dbb9e9a7..a3916f3f 100644 --- a/include/SSVOpenHexagon/Global/Config.hpp +++ b/include/SSVOpenHexagon/Global/Config.hpp @@ -83,6 +83,12 @@ void setSaveLastLoginUsername(bool mX); void setLastLoginUsername(const std::string& mX); void setShowLoginAtStartup(bool mX); void setCameraShakeMultiplier(float x); +void setAngleTiltIntensity(float x); +void setShowPlayerTrail(bool mX); +void setPlayerTrailAlpha(unsigned int x); +void setPlayerTrailScale(float x); +void setPlayerTrailDecay(float x); +void setPlayerTrailHasSwapColor(bool x); [[nodiscard]] bool getOfficial(); [[nodiscard]] const std::string& getUneligibilityReason(); @@ -150,6 +156,12 @@ void setCameraShakeMultiplier(float x); [[nodiscard]] const std::string& getLastLoginUsername(); [[nodiscard]] bool getShowLoginAtStartup(); [[nodiscard]] float getCameraShakeMultiplier(); +[[nodiscard]] float getAngleTiltIntensity(); +[[nodiscard]] bool getShowPlayerTrail(); +[[nodiscard]] unsigned int getPlayerTrailAlpha(); +[[nodiscard]] float getPlayerTrailScale(); +[[nodiscard]] float getPlayerTrailDecay(); +[[nodiscard]] bool getPlayerTrailHasSwapColor(); // keyboard binds diff --git a/include/SSVOpenHexagon/Global/Version.hpp b/include/SSVOpenHexagon/Global/Version.hpp index 6837b03a..4080a03e 100644 --- a/include/SSVOpenHexagon/Global/Version.hpp +++ b/include/SSVOpenHexagon/Global/Version.hpp @@ -43,7 +43,7 @@ struct GameVersion } }; -inline constexpr GameVersion GAME_VERSION{2, 1, 1}; -inline constexpr auto& GAME_VERSION_STR = "2.1.1"; +inline constexpr GameVersion GAME_VERSION{2, 1, 2}; +inline constexpr auto& GAME_VERSION_STR = "2.1.2"; } // namespace hg diff --git a/include/SSVOpenHexagon/Online/Shared.hpp b/include/SSVOpenHexagon/Online/Shared.hpp index 789d44bb..aa918875 100644 --- a/include/SSVOpenHexagon/Online/Shared.hpp +++ b/include/SSVOpenHexagon/Online/Shared.hpp @@ -143,8 +143,3 @@ template <typename T> sf::Packet& p); } // namespace hg - -// TODO (P1): leaderboards article on /r/gamedev -// - fast math issue -// - rng advance for graphical things -// - check devlogs diff --git a/include/SSVOpenHexagon/Utils/MoveTowards.hpp b/include/SSVOpenHexagon/Utils/MoveTowards.hpp new file mode 100644 index 00000000..55c4525a --- /dev/null +++ b/include/SSVOpenHexagon/Utils/MoveTowards.hpp @@ -0,0 +1,57 @@ +// Copyright (c) 2013-2020 Vittorio Romeo +// License: Academic Free License ("AFL") v. 3.0 +// AFL License page: https://opensource.org/licenses/AFL-3.0 + +#pragma once + +#include "SSVOpenHexagon/Global/Assert.hpp" + +#include <algorithm> +#include <cmath> + +namespace hg::Utils { + +[[nodiscard, gnu::always_inline, gnu::const]] inline constexpr float +getMoveTowards(float value, const float target, const float step) noexcept +{ + SSVOH_ASSERT(step >= 0); + + if(value < target) + { + value += step; + if(value > target) + { + value = target; + } + } + else if(value > target) + { + value -= step; + if(value < target) + { + value = target; + } + } + + return value; +} + +[[nodiscard, gnu::always_inline, gnu::const]] inline constexpr float +getMoveTowardsZero(const float value, const float step) noexcept +{ + return getMoveTowards(value, 0.f, step); +} + +[[gnu::always_inline]] inline constexpr void moveTowards( + float& value, const float target, const float step) noexcept +{ + value = getMoveTowards(value, target, step); +} + +[[gnu::always_inline]] inline constexpr void moveTowardsZero( + float& value, const float step) noexcept +{ + value = getMoveTowardsZero(value, step); +} + +} // namespace hg::Utils diff --git a/prepare_release.sh b/prepare_release.sh index d43469db..731c3bb2 100644 --- a/prepare_release.sh +++ b/prepare_release.sh @@ -17,6 +17,7 @@ cp -r ./_RELEASE/Packs/orthoplex ./_PREPARED_RELEASE/Packs mkdir -p ./_PREPARED_RELEASE/Profiles cp ./_RELEASE/SSVOpenHexagon.exe ./_PREPARED_RELEASE +cp ./_RELEASE/SSVOpenHexagon-Console.exe ./_PREPARED_RELEASE cp ./_RELEASE/libzlib1.dll ./_PREPARED_RELEASE cp ./_RELEASE/openal32.dll ./_PREPARED_RELEASE @@ -47,4 +48,9 @@ cp ./_RELEASE/noaudio.bat ./_PREPARED_RELEASE cp ./_RELEASE/OHWorkshopUploader.exe ./_PREPARED_RELEASE +cd ./_PREPARED_RELEASE +upx -9 ./*.dll +upx -9 ./*.exe +cd .. + cp -r ./_PREPARED_RELEASE ./_PREPARED_RELEASE_TEST diff --git a/public/sqlite_orm/sqlite_orm.h b/public/sqlite_orm/sqlite_orm.h index a591940c..67df39aa 100644 --- a/public/sqlite_orm/sqlite_orm.h +++ b/public/sqlite_orm/sqlite_orm.h @@ -18,6 +18,8 @@ __pragma(push_macro("min")) #if __cplusplus >= 201703L // use of C++17 or higher // Enables use of std::optional in SQLITE_ORM. #define SQLITE_ORM_OPTIONAL_SUPPORTED +#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED #endif #pragma once @@ -44,6 +46,10 @@ __pragma(push_macro("min")) failed_to_init_a_backup, unknown_member_value, incorrect_order, + cannot_use_default_value, + arguments_count_does_not_match, + function_not_found, + index_is_out_of_bounds, }; } @@ -51,7 +57,7 @@ namespace sqlite_orm { class orm_error_category : public std::error_category { public: - const char *name() const noexcept override final { + const char* name() const noexcept override final { return "ORM error"; } @@ -83,6 +89,14 @@ namespace sqlite_orm { return "Unknown member value"; case orm_error_code::incorrect_order: return "Incorrect order"; + case orm_error_code::cannot_use_default_value: + return "The statement 'INSERT INTO * DEFAULT VALUES' can be used with only one row"; + case orm_error_code::arguments_count_does_not_match: + return "Arguments count does not match"; + case orm_error_code::function_not_found: + return "Function not found"; + case orm_error_code::index_is_out_of_bounds: + return "Index is out of bounds"; default: return "unknown error"; } @@ -91,7 +105,7 @@ namespace sqlite_orm { class sqlite_error_category : public std::error_category { public: - const char *name() const noexcept override final { + const char* name() const noexcept override final { return "SQLite error"; } @@ -100,18 +114,18 @@ namespace sqlite_orm { } }; - inline const orm_error_category &get_orm_error_category() { + inline const orm_error_category& get_orm_error_category() { static orm_error_category res; return res; } - inline const sqlite_error_category &get_sqlite_error_category() { + inline const sqlite_error_category& get_sqlite_error_category() { static sqlite_error_category res; return res; } template<typename... T> - std::string get_error_message(sqlite3 *db, T &&... args) { + std::string get_error_message(sqlite3* db, T&&... args) { std::ostringstream stream; using unpack = int[]; static_cast<void>(unpack{0, (static_cast<void>(static_cast<void>(stream << args)), 0)...}); @@ -120,7 +134,7 @@ namespace sqlite_orm { } template<typename... T> - [[noreturn]] void throw_error(sqlite3 *db, T &&... args) { + [[noreturn]] void throw_error(sqlite3* db, T&&... args) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), get_error_message(db, std::forward<T>(args)...)); } @@ -136,7 +150,7 @@ namespace std { } #pragma once -#include <tuple> // std::tuple, std::get +#include <tuple> // std::tuple, std::get, std::tuple_element, std::tuple_size #include <type_traits> // std::false_type, std::true_type // #include "static_magic.h" @@ -150,27 +164,27 @@ namespace sqlite_orm { namespace internal { static inline decltype(auto) empty_callable() { - static auto res = [](auto &&...) {}; + static auto res = [](auto&&...) {}; return (res); } template<typename T, typename F> - decltype(auto) static_if(std::true_type, const T &t, const F &) { + decltype(auto) static_if(std::true_type, const T& t, const F&) { return (t); } template<typename T, typename F> - decltype(auto) static_if(std::false_type, const T &, const F &f) { + decltype(auto) static_if(std::false_type, const T&, const F& f) { return (f); } template<bool B, typename T, typename F> - decltype(auto) static_if(const T &t, const F &f) { + decltype(auto) static_if(const T& t, const F& f) { return static_if(std::integral_constant<bool, B>{}, t, f); } template<bool B, typename T> - decltype(auto) static_if(const T &t) { + decltype(auto) static_if(const T& t) { return static_if(std::integral_constant<bool, B>{}, t, empty_callable()); } @@ -185,6 +199,9 @@ namespace sqlite_orm { // got from here http://stackoverflow.com/questions/25958259/how-do-i-find-out-if-a-tuple-contains-a-type namespace tuple_helper { + /** + * HAS_TYPE type trait + */ template<typename T, typename Tuple> struct has_type; @@ -200,62 +217,152 @@ namespace sqlite_orm { template<typename T, typename Tuple> using tuple_contains_type = typename has_type<T, Tuple>::type; + /** + * HAS_SOME_TYPE type trait + */ + template<template<class> class TT, typename Tuple> + struct has_some_type; + + template<template<class> class TT> + struct has_some_type<TT, std::tuple<>> : std::false_type {}; + + template<template<class> class TT, typename U, typename... Ts> + struct has_some_type<TT, std::tuple<U, Ts...>> : has_some_type<TT, std::tuple<Ts...>> {}; + + template<template<class> class TT, typename T, typename... Ts> + struct has_some_type<TT, std::tuple<TT<T>, Ts...>> : std::true_type {}; + + template<template<class> class TT, typename Tuple> + using tuple_contains_some_type = typename has_some_type<TT, Tuple>::type; + template<size_t N, class... Args> - struct iterator { + struct iterator_impl { template<class L> - void operator()(const std::tuple<Args...> &t, const L &l, bool reverse = true) { + void operator()(const std::tuple<Args...>& tuple, const L& lambda, bool reverse = true) { if(reverse) { - l(std::get<N>(t)); - iterator<N - 1, Args...>()(t, l, reverse); + lambda(std::get<N>(tuple)); + iterator_impl<N - 1, Args...>()(tuple, lambda, reverse); } else { - iterator<N - 1, Args...>()(t, l, reverse); - l(std::get<N>(t)); + iterator_impl<N - 1, Args...>()(tuple, lambda, reverse); + lambda(std::get<N>(tuple)); } } + + template<class L> + void operator()(const L& lambda) { + iterator_impl<N - 1, Args...>()(lambda); + lambda((const typename std::tuple_element<N - 1, std::tuple<Args...>>::type*)nullptr); + } }; template<class... Args> - struct iterator<0, Args...> { + struct iterator_impl<0, Args...> { + + template<class L> + void operator()(const std::tuple<Args...>& tuple, const L& lambda, bool /*reverse*/ = true) { + lambda(std::get<0>(tuple)); + } template<class L> - void operator()(const std::tuple<Args...> &t, const L &l, bool /*reverse*/ = true) { - l(std::get<0>(t)); + void operator()(const L& lambda) { + lambda((const typename std::tuple_element<0, std::tuple<Args...>>::type*)nullptr); } }; template<size_t N> - struct iterator<N> { + struct iterator_impl<N> { + + template<class L> + void operator()(const std::tuple<>&, const L&, bool /*reverse*/ = true) { + //.. + } + + template<class L> + void operator()(const L&) { + //.. + } + }; + + template<class... Args> + struct iterator_impl2; + + template<> + struct iterator_impl2<> { template<class L> - void operator()(const std::tuple<> &, const L &, bool /*reverse*/ = true) { + void operator()(const L&) const { //.. } }; + template<class H, class... Tail> + struct iterator_impl2<H, Tail...> { + + template<class L> + void operator()(const L& lambda) const { + lambda((const H*)nullptr); + iterator_impl2<Tail...>{}(lambda); + } + }; + + template<class T> + struct iterator; + + template<class... Args> + struct iterator<std::tuple<Args...>> { + + template<class L> + void operator()(const L& lambda) const { + iterator_impl2<Args...>{}(lambda); + } + }; + } + + namespace internal { + + // got it form here https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer + template<class Function, class FunctionPointer, class Tuple, size_t... I> + auto call_impl(Function& f, FunctionPointer functionPointer, Tuple t, std::index_sequence<I...>) { + return (f.*functionPointer)(std::get<I>(t)...); + } + + template<class Function, class FunctionPointer, class Tuple> + auto call(Function& f, FunctionPointer functionPointer, Tuple t) { + static constexpr auto size = std::tuple_size<Tuple>::value; + return call_impl(f, functionPointer, move(t), std::make_index_sequence<size>{}); + } + + template<class Function, class Tuple> + auto call(Function& f, Tuple t) { + return call(f, &Function::operator(), move(t)); + } + template<size_t N, size_t I, class L, class R> - void move_tuple_impl(L &lhs, R &rhs) { + void move_tuple_impl(L& lhs, R& rhs) { std::get<I>(lhs) = std::move(std::get<I>(rhs)); - internal::static_if<std::integral_constant<bool, N != I + 1>{}>([](auto &l, auto &r) { + internal::static_if<std::integral_constant<bool, N != I + 1>{}>([](auto& l, auto& r) { move_tuple_impl<N, I + 1>(l, r); })(lhs, rhs); } - } - - namespace internal { template<size_t N, class L, class R> - void move_tuple(L &lhs, R &rhs) { + void move_tuple(L& lhs, R& rhs) { using bool_type = std::integral_constant<bool, N != 0>; - static_if<bool_type{}>([](auto &l, auto &r) { - tuple_helper::move_tuple_impl<N, 0>(l, r); + static_if<bool_type{}>([](auto& l, auto& r) { + move_tuple_impl<N, 0>(l, r); })(lhs, rhs); } template<class L, class... Args> - void iterate_tuple(const std::tuple<Args...> &t, const L &l) { + void iterate_tuple(const std::tuple<Args...>& tuple, const L& lambda) { using tuple_type = std::tuple<Args...>; - tuple_helper::iterator<std::tuple_size<tuple_type>::value - 1, Args...>()(t, l, false); + tuple_helper::iterator_impl<std::tuple_size<tuple_type>::value - 1, Args...>()(tuple, lambda, false); + } + + template<class T, class L> + void iterate_tuple(const L& lambda) { + tuple_helper::iterator<T>{}(lambda); } template<typename... input_t> @@ -265,6 +372,57 @@ namespace sqlite_orm { struct conc_tuple { using type = tuple_cat_t<Args...>; }; + } +} +#pragma once + +#include <tuple> // std::tuple +#include <type_traits> // std::enable_if + +namespace sqlite_orm { + namespace internal { + + template<class T, template<class> class C, class SFINAE = void> + struct find_in_tuple; + + template<template<class> class C> + struct find_in_tuple<std::tuple<>, C, void> { + using type = void; + }; + + template<class H, class... Args, template<class> class C> + struct find_in_tuple<std::tuple<H, Args...>, C, typename std::enable_if<C<H>::value>::type> { + using type = H; + }; + + template<class H, class... Args, template<class> class C> + struct find_in_tuple<std::tuple<H, Args...>, C, typename std::enable_if<!C<H>::value>::type> { + using type = typename find_in_tuple<std::tuple<Args...>, C>::type; + }; + } +} +#pragma once + +#include <tuple> // std::tuple + +namespace sqlite_orm { + namespace internal { + + template<class T, template<class C> class F> + struct tuple_transformer; + + template<class... Args, template<class C> class F> + struct tuple_transformer<std::tuple<Args...>, F> { + using type = std::tuple<typename F<Args>::type...>; + }; + } +} +#pragma once + +#include <tuple> // std::tuple + +namespace sqlite_orm { + namespace internal { template<class T, template<class> class C> struct count_tuple; @@ -282,6 +440,41 @@ namespace sqlite_orm { } #pragma once +namespace sqlite_orm { + namespace internal { + + /** + * Accepts any number of arguments and evaluates `type` alias as T if all arguments are the same or void otherwise + */ + template<class... Args> + struct same_or_void { + using type = void; + }; + + template<class A> + struct same_or_void<A> { + using type = A; + }; + + template<class A, class B> + struct same_or_void<A, B> { + using type = void; + }; + + template<class A> + struct same_or_void<A, A> { + using type = A; + }; + + template<class A, class... Args> + struct same_or_void<A, A, Args...> { + using type = typename same_or_void<A, Args...>::type; + }; + + } +} +#pragma once + #include <string> // std::string #include <memory> // std::shared_ptr, std::unique_ptr #include <vector> // std::vector @@ -298,28 +491,28 @@ namespace sqlite_orm { struct type_printer; struct integer_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "INTEGER"; return res; } }; struct text_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "TEXT"; return res; } }; struct real_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "REAL"; return res; } }; struct blob_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "BLOB"; return res; } @@ -369,7 +562,7 @@ namespace sqlite_orm { struct type_printer<std::wstring, void> : public text_printer {}; template<> - struct type_printer<const char *, void> : public text_printer {}; + struct type_printer<const char*, void> : public text_printer {}; template<> struct type_printer<float, void> : public real_printer {}; @@ -413,9 +606,364 @@ namespace sqlite_orm { #include <type_traits> // std::is_base_of, std::false_type, std::true_type #include <ostream> // std::ostream +// #include "table_type.h" + +#include <type_traits> // std::enable_if, std::is_member_pointer, std::is_member_function_pointer + +// #include "member_traits/getter_traits.h" + +// #include "getters.h" + +namespace sqlite_orm { + namespace internal { + + template<class O, class T> + using getter_by_value_const = T (O::*)() const; + + template<class O, class T> + using getter_by_value = T (O::*)(); + + template<class O, class T> + using getter_by_ref_const = T& (O::*)() const; + + template<class O, class T> + using getter_by_ref = T& (O::*)(); + + template<class O, class T> + using getter_by_const_ref_const = const T& (O::*)() const; + + template<class O, class T> + using getter_by_const_ref = const T& (O::*)(); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template<class O, class T> + using getter_by_value_const_noexcept = T (O::*)() const noexcept; + + template<class O, class T> + using getter_by_value_noexcept = T (O::*)() noexcept; + + template<class O, class T> + using getter_by_ref_const_noexcept = T& (O::*)() const noexcept; + + template<class O, class T> + using getter_by_ref_noexcept = T& (O::*)() noexcept; + + template<class O, class T> + using getter_by_const_ref_const_noexcept = const T& (O::*)() const noexcept; + + template<class O, class T> + using getter_by_const_ref_noexcept = const T& (O::*)() noexcept; +#endif + } +} + namespace sqlite_orm { + namespace internal { + + template<class T> + struct getter_traits; + + template<class O, class T> + struct getter_traits<getter_by_value_const<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; - namespace constraints { + template<class O, class T> + struct getter_traits<getter_by_value<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template<class O, class T> + struct getter_traits<getter_by_ref_const<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template<class O, class T> + struct getter_traits<getter_by_ref<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template<class O, class T> + struct getter_traits<getter_by_const_ref_const<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template<class O, class T> + struct getter_traits<getter_by_const_ref<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template<class O, class T> + struct getter_traits<getter_by_value_const_noexcept<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template<class O, class T> + struct getter_traits<getter_by_value_noexcept<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template<class O, class T> + struct getter_traits<getter_by_ref_const_noexcept<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template<class O, class T> + struct getter_traits<getter_by_ref_noexcept<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template<class O, class T> + struct getter_traits<getter_by_const_ref_const_noexcept<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template<class O, class T> + struct getter_traits<getter_by_const_ref_noexcept<O, T>> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; +#endif + } +} + +// #include "member_traits/setter_traits.h" + +// #include "setters.h" + +namespace sqlite_orm { + namespace internal { + + template<class O, class T> + using setter_by_value = void (O::*)(T); + + template<class O, class T> + using setter_by_ref = void (O::*)(T&); + + template<class O, class T> + using setter_by_const_ref = void (O::*)(const T&); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template<class O, class T> + using setter_by_value_noexcept = void (O::*)(T) noexcept; + + template<class O, class T> + using setter_by_ref_noexcept = void (O::*)(T&) noexcept; + + template<class O, class T> + using setter_by_const_ref_noexcept = void (O::*)(const T&) noexcept; +#endif + } +} + +namespace sqlite_orm { + namespace internal { + + template<class T> + struct setter_traits; + + template<class O, class T> + struct setter_traits<setter_by_value<O, T>> { + using object_type = O; + using field_type = T; + }; + + template<class O, class T> + struct setter_traits<setter_by_ref<O, T>> { + using object_type = O; + using field_type = T; + }; + + template<class O, class T> + struct setter_traits<setter_by_const_ref<O, T>> { + using object_type = O; + using field_type = T; + }; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template<class O, class T> + struct setter_traits<setter_by_value_noexcept<O, T>> { + using object_type = O; + using field_type = T; + }; + + template<class O, class T> + struct setter_traits<setter_by_ref_noexcept<O, T>> { + using object_type = O; + using field_type = T; + }; + + template<class O, class T> + struct setter_traits<setter_by_const_ref_noexcept<O, T>> { + using object_type = O; + using field_type = T; + }; +#endif + } +} + +// #include "member_traits/is_getter.h" + +#include <type_traits> // std::false_type, std::true_type + +// #include "getters.h" + +namespace sqlite_orm { + namespace internal { + + template<class T> + struct is_getter : std::false_type {}; + + template<class O, class T> + struct is_getter<getter_by_value_const<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_value<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_ref_const<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_ref<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_const_ref_const<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_const_ref<O, T>> : std::true_type {}; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template<class O, class T> + struct is_getter<getter_by_value_const_noexcept<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_value_noexcept<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_ref_const_noexcept<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_ref_noexcept<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_const_ref_const_noexcept<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_getter<getter_by_const_ref_noexcept<O, T>> : std::true_type {}; +#endif + } +} + +// #include "member_traits/is_setter.h" + +// #include "setters.h" + +namespace sqlite_orm { + namespace internal { + + template<class T> + struct is_setter : std::false_type {}; + + template<class O, class T> + struct is_setter<setter_by_value<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_setter<setter_by_ref<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_setter<setter_by_const_ref<O, T>> : std::true_type {}; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template<class O, class T> + struct is_setter<setter_by_value_noexcept<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_setter<setter_by_ref_noexcept<O, T>> : std::true_type {}; + + template<class O, class T> + struct is_setter<setter_by_const_ref_noexcept<O, T>> : std::true_type {}; +#endif + } +} + +namespace sqlite_orm { + + namespace internal { + + template<class T, class F> + struct column_pointer; + + /** + * Trait class used to define table mapped type by setter/getter/member + * T - member pointer + * `type` is a type which is mapped. + * E.g. + * - `table_type<decltype(&User::id)>::type` is `User` + * - `table_type<decltype(&User::getName)>::type` is `User` + * - `table_type<decltype(&User::setName)>::type` is `User` + */ + template<class T, class SFINAE = void> + struct table_type; + + template<class O, class F> + struct table_type<F O::*, + typename std::enable_if<std::is_member_pointer<F O::*>::value && + !std::is_member_function_pointer<F O::*>::value>::type> { + using type = O; + }; + + template<class T> + struct table_type<T, typename std::enable_if<is_getter<T>::value>::type> { + using type = typename getter_traits<T>::object_type; + }; + + template<class T> + struct table_type<T, typename std::enable_if<is_setter<T>::value>::type> { + using type = typename setter_traits<T>::object_type; + }; + + template<class T, class F> + struct table_type<column_pointer<T, F>, void> { + using type = T; + }; + } +} + +// #include "tuple_helper/tuple_helper.h" + +namespace sqlite_orm { + + namespace internal { /** * AUTOINCREMENT constraint class. @@ -455,7 +1003,7 @@ namespace sqlite_orm { /** * PRIMARY KEY constraint class. * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when - * used withen `make_column` function. + * used withen `make_column` function. */ template<class... Cs> struct primary_key_t : primary_key_base { @@ -533,7 +1081,7 @@ namespace sqlite_orm { cascade, }; - inline std::ostream &operator<<(std::ostream &os, foreign_key_action action) { + inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { switch(action) { case decltype(action)::no_action: os << "NO ACTION"; @@ -575,7 +1123,7 @@ namespace sqlite_orm { struct on_update_delete_t : on_update_delete_base { using foreign_key_type = F; - const foreign_key_type &fk; + const foreign_key_type& fk; on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : on_update_delete_base{update_}, fk(fk_), _action(action_) {} @@ -637,12 +1185,27 @@ namespace sqlite_orm { } }; + template<class F> + bool operator==(const on_update_delete_t<F>& lhs, const on_update_delete_t<F>& rhs) { + return lhs._action == rhs._action; + } + template<class... Cs, class... Rs> struct foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>> { using columns_type = std::tuple<Cs...>; using references_type = std::tuple<Rs...>; using self = foreign_key_t<columns_type, references_type>; + /** + * Holds obect type of all referenced columns. + */ + using target_type = typename same_or_void<typename table_type<Rs>::type...>::type; + + /** + * Holds obect type of all source columns. + */ + using source_type = typename same_or_void<typename table_type<Cs>::type...>::type; + columns_type columns; references_type references; @@ -651,16 +1214,17 @@ namespace sqlite_orm { static_assert(std::tuple_size<columns_type>::value == std::tuple_size<references_type>::value, "Columns size must be equal to references tuple"); + static_assert(!std::is_same<target_type, void>::value, "All references must have the same type"); foreign_key_t(columns_type columns_, references_type references_) : - columns(std::move(columns_)), references(std::move(references_)), + columns(move(columns_)), references(move(references_)), on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} - foreign_key_t(const self &other) : + foreign_key_t(const self& other) : columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), on_delete(*this, false, other.on_delete._action) {} - self &operator=(const self &other) { + self& operator=(const self& other) { this->columns = other.columns; this->references = other.references; this->on_update = {*this, true, other.on_update._action}; @@ -669,7 +1233,7 @@ namespace sqlite_orm { } template<class L> - void for_each_column(const L &) {} + void for_each_column(const L&) {} template<class... Opts> constexpr bool has_every() const { @@ -677,6 +1241,12 @@ namespace sqlite_orm { } }; + template<class A, class B> + bool operator==(const foreign_key_t<A, B>& lhs, const foreign_key_t<A, B>& rhs) { + return lhs.columns == rhs.columns && lhs.references == rhs.references && lhs.on_update == rhs.on_update && + lhs.on_delete == rhs.on_delete; + } + /** * Cs can be a class member pointer, a getter function member pointer or setter * func member pointer @@ -697,10 +1267,10 @@ namespace sqlite_orm { }; #endif - struct collate_t { + struct collate_constraint_t { internal::collate_argument argument = internal::collate_argument::binary; - collate_t(internal::collate_argument argument_) : argument(argument_) {} + collate_constraint_t(internal::collate_argument argument_) : argument(argument_) {} operator std::string() const { std::string res = "COLLATE " + this->string_from_collate_argument(this->argument); @@ -754,7 +1324,7 @@ namespace sqlite_orm { struct is_constraint<foreign_key_t<C, R>> : std::true_type {}; template<> - struct is_constraint<collate_t> : std::true_type {}; + struct is_constraint<collate_constraint_t> : std::true_type {}; template<class T> struct is_constraint<check_t<T>> : std::true_type {}; @@ -780,7 +1350,7 @@ namespace sqlite_orm { * Available in SQLite 3.6.19 or higher */ template<class... Cs> - constraints::foreign_key_intermediate_t<Cs...> foreign_key(Cs... columns) { + internal::foreign_key_intermediate_t<Cs...> foreign_key(Cs... columns) { return {std::make_tuple(std::forward<Cs>(columns)...)}; } #endif @@ -789,46 +1359,46 @@ namespace sqlite_orm { * UNIQUE constraint builder function. */ template<class... Args> - constraints::unique_t<Args...> unique(Args... args) { + internal::unique_t<Args...> unique(Args... args) { return {std::make_tuple(std::forward<Args>(args)...)}; } - inline constraints::unique_t<> unique() { + inline internal::unique_t<> unique() { return {{}}; } - inline constraints::autoincrement_t autoincrement() { + inline internal::autoincrement_t autoincrement() { return {}; } template<class... Cs> - constraints::primary_key_t<Cs...> primary_key(Cs... cs) { + internal::primary_key_t<Cs...> primary_key(Cs... cs) { return {std::make_tuple(std::forward<Cs>(cs)...)}; } - inline constraints::primary_key_t<> primary_key() { + inline internal::primary_key_t<> primary_key() { return {{}}; } template<class T> - constraints::default_t<T> default_value(T t) { + internal::default_t<T> default_value(T t) { return {std::move(t)}; } - inline constraints::collate_t collate_nocase() { + inline internal::collate_constraint_t collate_nocase() { return {internal::collate_argument::nocase}; } - inline constraints::collate_t collate_binary() { + inline internal::collate_constraint_t collate_binary() { return {internal::collate_argument::binary}; } - inline constraints::collate_t collate_rtrim() { + inline internal::collate_constraint_t collate_rtrim() { return {internal::collate_argument::rtrim}; } template<class T> - constraints::check_t<T> check(T t) { + internal::check_t<T> check(T t) { return {std::move(t)}; } @@ -844,7 +1414,7 @@ namespace sqlite_orm { * FOREIGN KEY traits. Specialized case */ template<class C, class R> - struct is_foreign_key<constraints::foreign_key_t<C, R>> : std::true_type {}; + struct is_foreign_key<internal::foreign_key_t<C, R>> : std::true_type {}; /** * PRIMARY KEY traits. Common case @@ -856,7 +1426,24 @@ namespace sqlite_orm { * PRIMARY KEY traits. Specialized case */ template<class... Cs> - struct is_primary_key<constraints::primary_key_t<Cs...>> : public std::true_type {}; + struct is_primary_key<internal::primary_key_t<Cs...>> : public std::true_type {}; + + /** + * PRIMARY KEY INSERTABLE traits. + */ + template<typename T> + struct is_primary_key_insertable { + using field_type = typename T::field_type; + using constraints_type = typename T::constraints_type; + + static_assert((tuple_helper::tuple_contains_type<primary_key_t<>, constraints_type>::value), + "an unexpected type was passed"); + + static constexpr bool value = + (tuple_helper::tuple_contains_some_type<default_t, constraints_type>::value || + tuple_helper::tuple_contains_type<autoincrement_t, constraints_type>::value || + std::is_base_of<integer_printer, type_printer<field_type>>::value); + }; } } @@ -879,7 +1466,7 @@ namespace sqlite_orm { */ template<class T> struct type_is_nullable : public std::false_type { - bool operator()(const T &) const { + bool operator()(const T&) const { return true; } }; @@ -889,7 +1476,7 @@ namespace sqlite_orm { */ template<class T> struct type_is_nullable<std::shared_ptr<T>> : public std::true_type { - bool operator()(const std::shared_ptr<T> &t) const { + bool operator()(const std::shared_ptr<T>& t) const { return static_cast<bool>(t); } }; @@ -899,7 +1486,7 @@ namespace sqlite_orm { */ template<class T> struct type_is_nullable<std::unique_ptr<T>> : public std::true_type { - bool operator()(const std::unique_ptr<T> &t) const { + bool operator()(const std::unique_ptr<T>& t) const { return static_cast<bool>(t); } }; @@ -910,7 +1497,7 @@ namespace sqlite_orm { */ template<class T> struct type_is_nullable<std::optional<T>> : public std::true_type { - bool operator()(const std::optional<T> &t) const { + bool operator()(const std::optional<T>& t) const { return t.has_value(); } }; @@ -937,8 +1524,8 @@ namespace sqlite_orm { bool use_parentheses = true; template<class O, class F> - std::string column_name(F O::*) const { - return {}; + const std::string* column_name(F O::*) const { + return nullptr; } }; @@ -946,12 +1533,12 @@ namespace sqlite_orm { struct serializator_context : serializator_context_base { using impl_type = I; - const impl_type &impl; + const impl_type& impl; - serializator_context(const impl_type &impl_) : impl(impl_) {} + serializator_context(const impl_type& impl_) : impl(impl_) {} template<class O, class F> - std::string column_name(F O::*m) const { + const std::string* column_name(F O::*m) const { return this->impl.column_name(m); } }; @@ -961,13 +1548,13 @@ namespace sqlite_orm { using storage_type = S; using impl_type = typename storage_type::impl_type; - serializator_context_builder(const storage_type &storage_) : storage(storage_) {} + serializator_context_builder(const storage_type& storage_) : storage(storage_) {} serializator_context<impl_type> operator()() const { return {this->storage.impl}; } - const storage_type &storage; + const storage_type& storage; }; } @@ -978,9 +1565,6 @@ namespace sqlite_orm { namespace internal { - template<class T> - std::string serialize(const T &t); - /** * This class is used in tuple interation to know whether tuple constains `default_value_t` * constraint class and what it's value if it is @@ -988,12 +1572,12 @@ namespace sqlite_orm { struct default_value_extractor { template<class A> - std::unique_ptr<std::string> operator()(const A &) { + std::unique_ptr<std::string> operator()(const A&) { return {}; } template<class T> - std::unique_ptr<std::string> operator()(const constraints::default_t<T> &t) { + std::unique_ptr<std::string> operator()(const default_t<T>& t) { serializator_context_base context; return std::make_unique<std::string>(serialize(t.value, context)); } @@ -1005,12 +1589,19 @@ namespace sqlite_orm { #pragma once #include <type_traits> // std::false_type, std::true_type - -// #include "negatable.h" +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include <optional> // std::nullopt +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "tags.h" namespace sqlite_orm { namespace internal { struct negatable_t {}; + + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; } } @@ -1094,7 +1685,7 @@ namespace sqlite_orm { template<class L, class R> using div_t = binary_operator<L, R, div_string, arithmetic_t, negatable_t>; - struct mod_string { + struct mod_operator_string { operator std::string() const { return "%"; } @@ -1104,7 +1695,7 @@ namespace sqlite_orm { * Result of mod % operator */ template<class L, class R> - using mod_t = binary_operator<L, R, mod_string, arithmetic_t, negatable_t>; + using mod_t = binary_operator<L, R, mod_operator_string, arithmetic_t, negatable_t>; struct bitwise_shift_left_string { operator std::string() const { @@ -1113,8 +1704,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise shift left << operator - */ + * Result of bitwise shift left << operator + */ template<class L, class R> using bitwise_shift_left_t = binary_operator<L, R, bitwise_shift_left_string, arithmetic_t, negatable_t>; @@ -1125,8 +1716,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise shift right >> operator - */ + * Result of bitwise shift right >> operator + */ template<class L, class R> using bitwise_shift_right_t = binary_operator<L, R, bitwise_shift_right_string, arithmetic_t, negatable_t>; @@ -1137,8 +1728,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise and & operator - */ + * Result of bitwise and & operator + */ template<class L, class R> using bitwise_and_t = binary_operator<L, R, bitwise_and_string, arithmetic_t, negatable_t>; @@ -1149,8 +1740,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise or | operator - */ + * Result of bitwise or | operator + */ template<class L, class R> using bitwise_or_t = binary_operator<L, R, bitwise_or_string, arithmetic_t, negatable_t>; @@ -1161,8 +1752,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise not ~ operator - */ + * Result of bitwise not ~ operator + */ template<class T> struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { using argument_type = T; @@ -1177,6 +1768,7 @@ namespace sqlite_orm { return "="; } }; + /** * Result of assign = operator */ @@ -1195,34 +1787,9 @@ namespace sqlite_orm { template<class L, class R> struct is_assign_t<assign_t<L, R>> : public std::true_type {}; - /** - * Is not an operator but a result of c(...) function. Has operator= overloaded which returns assign_t - */ - template<class T> - struct expression_t { - T t; - - expression_t(T t_) : t(std::move(t_)) {} - - template<class R> - assign_t<T, R> operator=(R r) const { - return {this->t, std::move(r)}; - } - - assign_t<T, std::nullptr_t> operator=(std::nullptr_t) const { - return {this->t, nullptr}; - } - }; - - } + template<class L, class... Args> + struct in_t; - /** - * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or - * `storage.update(set(c(&User::name) = "Dua Lipa")); - */ - template<class T> - internal::expression_t<T> c(T t) { - return {std::move(t)}; } /** @@ -1243,23 +1810,34 @@ namespace sqlite_orm { } /** - * Public interface for - operator. Example: `select(add(&User::age, 1));` => SELECT age - 1 FROM users + * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users */ template<class L, class R> internal::sub_t<L, R> sub(L l, R r) { return {std::move(l), std::move(r)}; } + /** + * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users + */ template<class L, class R> internal::mul_t<L, R> mul(L l, R r) { return {std::move(l), std::move(r)}; } + /** + * Public interface for / operator. Example: `select(div(&User::salary, 3));` => SELECT salary / 3 FROM users + * @note Please notice that ::div function already exists in pure C standard library inside <cstdlib> header. + * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. + */ template<class L, class R> internal::div_t<L, R> div(L l, R r) { return {std::move(l), std::move(r)}; } + /** + * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users + */ template<class L, class R> internal::mod_t<L, R> mod(L l, R r) { return {std::move(l), std::move(r)}; @@ -1305,16 +1883,21 @@ namespace sqlite_orm { // #include "type_is_nullable.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "default_value_extractor.h" // #include "constraints.h" -// #include "getter_traits.h" +// #include "member_traits/member_traits.h" -namespace sqlite_orm { +#include <type_traits> // std::enable_if +// #include "is_field_member_pointer.h" + +#include <type_traits> // std::false_type, std::true_type, std::is_member_pointer, std::is_member_function_pointer + +namespace sqlite_orm { namespace internal { template<class T, class SFINAE = void> @@ -1325,153 +1908,39 @@ namespace sqlite_orm { typename std::enable_if<std::is_member_pointer<T>::value && !std::is_member_function_pointer<T>::value>::type> : std::true_type {}; + } +} - template<class T, class SFINAE = void> - struct field_member_traits; - - template<class O, class F> - struct field_member_traits<F O::*, typename std::enable_if<is_field_member_pointer<F O::*>::value>::type> { - using object_type = O; - using field_type = F; - }; - - /** - * Getters aliases - */ - template<class O, class T> - using getter_by_value_const = T (O::*)() const; - - template<class O, class T> - using getter_by_value = T (O::*)(); - - template<class O, class T> - using getter_by_ref_const = T &(O::*)() const; - - template<class O, class T> - using getter_by_ref = T &(O::*)(); - - template<class O, class T> - using getter_by_const_ref_const = const T &(O::*)() const; - - template<class O, class T> - using getter_by_const_ref = const T &(O::*)(); - - /** - * Setters aliases - */ - template<class O, class T> - using setter_by_value = void (O::*)(T); - - template<class O, class T> - using setter_by_ref = void (O::*)(T &); - - template<class O, class T> - using setter_by_const_ref = void (O::*)(const T &); - - template<class T> - struct is_getter : std::false_type {}; - - template<class O, class T> - struct is_getter<getter_by_value_const<O, T>> : std::true_type {}; - - template<class O, class T> - struct is_getter<getter_by_value<O, T>> : std::true_type {}; - - template<class O, class T> - struct is_getter<getter_by_ref_const<O, T>> : std::true_type {}; - - template<class O, class T> - struct is_getter<getter_by_ref<O, T>> : std::true_type {}; - - template<class O, class T> - struct is_getter<getter_by_const_ref_const<O, T>> : std::true_type {}; - - template<class O, class T> - struct is_getter<getter_by_const_ref<O, T>> : std::true_type {}; - - template<class T> - struct is_setter : std::false_type {}; - - template<class O, class T> - struct is_setter<setter_by_value<O, T>> : std::true_type {}; - - template<class O, class T> - struct is_setter<setter_by_ref<O, T>> : std::true_type {}; - - template<class O, class T> - struct is_setter<setter_by_const_ref<O, T>> : std::true_type {}; - - template<class T> - struct getter_traits; - - template<class O, class T> - struct getter_traits<getter_by_value_const<O, T>> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; - }; - - template<class O, class T> - struct getter_traits<getter_by_value<O, T>> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; - }; - - template<class O, class T> - struct getter_traits<getter_by_ref_const<O, T>> { - using object_type = O; - using field_type = T; +// #include "is_getter.h" - static constexpr const bool returns_lvalue = true; - }; +// #include "field_member_traits.h" - template<class O, class T> - struct getter_traits<getter_by_ref<O, T>> { - using object_type = O; - using field_type = T; +#include <type_traits> // std::enable_if - static constexpr const bool returns_lvalue = true; - }; +// #include "is_field_member_pointer.h" - template<class O, class T> - struct getter_traits<getter_by_const_ref_const<O, T>> { - using object_type = O; - using field_type = T; +namespace sqlite_orm { + namespace internal { - static constexpr const bool returns_lvalue = true; - }; + template<class T, class SFINAE = void> + struct field_member_traits; - template<class O, class T> - struct getter_traits<getter_by_const_ref<O, T>> { + template<class O, class F> + struct field_member_traits<F O::*, typename std::enable_if<is_field_member_pointer<F O::*>::value>::type> { using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; + using field_type = F; }; + } +} - template<class T> - struct setter_traits; +// #include "is_setter.h" - template<class O, class T> - struct setter_traits<setter_by_value<O, T>> { - using object_type = O; - using field_type = T; - }; +// #include "getter_traits.h" - template<class O, class T> - struct setter_traits<setter_by_ref<O, T>> { - using object_type = O; - using field_type = T; - }; +// #include "setter_traits.h" - template<class O, class T> - struct setter_traits<setter_by_const_ref<O, T>> { - using object_type = O; - using field_type = T; - }; +namespace sqlite_orm { + namespace internal { template<class T, class SFINAE = void> struct member_traits; @@ -1500,7 +1969,7 @@ namespace sqlite_orm { namespace internal { - struct column_base { + struct basic_column { /** * Column name. Specified during construction in `make_column`. @@ -1515,7 +1984,7 @@ namespace sqlite_orm { * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc */ template<class O, class T, class G /* = const T& (O::*)() const*/, class S /* = void (O::*)(T)*/, class... Op> - struct column_t : column_base { + struct column_t : basic_column { using object_type = O; using field_type = T; using constraints_type = std::tuple<Op...>; @@ -1549,7 +2018,7 @@ namespace sqlite_orm { getter_type getter_, setter_type setter_, constraints_type constraints_) : - column_base{std::move(name_)}, + basic_column{move(name_)}, member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {} /** @@ -1584,7 +2053,7 @@ namespace sqlite_orm { */ std::unique_ptr<std::string> default_value() const { std::unique_ptr<std::string> res; - iterate_tuple(this->constraints, [&res](auto &v) { + iterate_tuple(this->constraints, [&res](auto& v) { auto dft = internal::default_value_extractor()(v); if(dft) { res = std::move(dft); @@ -1594,6 +2063,48 @@ namespace sqlite_orm { } }; + // we are compelled to wrap all sfinae-implemented traits to prevent "error: type/value mismatch at argument 2 in template parameter list" + namespace sfinae { + /** + * Column with insertable primary key traits. Common case. + */ + template<class T, class SFINAE = void> + struct is_column_with_insertable_primary_key : public std::false_type {}; + + /** + * Column with insertable primary key traits. Specialized case case. + */ + template<class O, class T, class... Op> + struct is_column_with_insertable_primary_key< + column_t<O, T, Op...>, + typename std::enable_if<(tuple_helper::tuple_contains_type< + primary_key_t<>, + typename column_t<O, T, Op...>::constraints_type>::value)>::type> { + using column_type = column_t<O, T, Op...>; + static constexpr bool value = is_primary_key_insertable<column_type>::value; + }; + + /** + * Column with noninsertable primary key traits. Common case. + */ + template<class T, class SFINAE = void> + struct is_column_with_noninsertable_primary_key : public std::false_type {}; + + /** + * Column with noninsertable primary key traits. Specialized case case. + */ + template<class O, class T, class... Op> + struct is_column_with_noninsertable_primary_key< + column_t<O, T, Op...>, + typename std::enable_if<(tuple_helper::tuple_contains_type< + primary_key_t<>, + typename column_t<O, T, Op...>::constraints_type>::value)>::type> { + using column_type = column_t<O, T, Op...>; + static constexpr bool value = !is_primary_key_insertable<column_type>::value; + }; + + } + /** * Column traits. Common case. */ @@ -1606,6 +2117,18 @@ namespace sqlite_orm { template<class O, class T, class... Op> struct is_column<column_t<O, T, Op...>> : public std::true_type {}; + /** + * Column with insertable primary key traits. + */ + template<class T> + struct is_column_with_insertable_primary_key : public sfinae::is_column_with_insertable_primary_key<T> {}; + + /** + * Column with noninsertable primary key traits. + */ + template<class T> + struct is_column_with_noninsertable_primary_key : public sfinae::is_column_with_noninsertable_primary_key<T> {}; + template<class T> struct column_field_type { using type = void; @@ -1635,9 +2158,9 @@ namespace sqlite_orm { class T, typename = typename std::enable_if<!std::is_member_function_pointer<T O::*>::value>::type, class... Op> - internal::column_t<O, T, const T &(O::*)() const, void (O::*)(T), Op...> - make_column(const std::string &name, T O::*m, Op... constraints) { - static_assert(constraints::template constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, + internal::column_t<O, T, const T& (O::*)() const, void (O::*)(T), Op...> + make_column(const std::string& name, T O::*m, Op... constraints) { + static_assert(internal::template constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, "Incorrect constraints pack"); static_assert(internal::is_field_member_pointer<T O::*>::value, "second argument expected as a member field pointer, not member function pointer"); @@ -1657,11 +2180,11 @@ namespace sqlite_orm { G, S, Op...> - make_column(const std::string &name, S setter, G getter, Op... constraints) { + make_column(const std::string& name, S setter, G getter, Op... constraints) { static_assert(std::is_same<typename internal::setter_traits<S>::field_type, typename internal::getter_traits<G>::field_type>::value, "Getter and setter must get and set same data type"); - static_assert(constraints::template constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, + static_assert(internal::template constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } @@ -1680,11 +2203,11 @@ namespace sqlite_orm { G, S, Op...> - make_column(const std::string &name, G getter, S setter, Op... constraints) { + make_column(const std::string& name, G getter, S setter, Op... constraints) { static_assert(std::is_same<typename internal::setter_traits<S>::field_type, typename internal::getter_traits<G>::field_type>::value, "Getter and setter must get and set same data type"); - static_assert(constraints::template constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, + static_assert(internal::template constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } @@ -1707,9 +2230,9 @@ namespace sqlite_orm { * Is used to print members mapped to objects in storage_t::dump member function. * Other developers can create own specialization to map custom types */ - template<class T> + template<class T, typename Enable = void> struct field_printer { - std::string operator()(const T &t) const { + std::string operator()(const T& t) const { std::stringstream stream; stream << t; return stream.str(); @@ -1720,8 +2243,8 @@ namespace sqlite_orm { * Upgrade to integer is required when using unsigned char(uint8_t) */ template<> - struct field_printer<unsigned char> { - std::string operator()(const unsigned char &t) const { + struct field_printer<unsigned char, void> { + std::string operator()(const unsigned char& t) const { std::stringstream stream; stream << +t; return stream.str(); @@ -1732,8 +2255,8 @@ namespace sqlite_orm { * Upgrade to integer is required when using signed char(int8_t) */ template<> - struct field_printer<signed char> { - std::string operator()(const signed char &t) const { + struct field_printer<signed char, void> { + std::string operator()(const signed char& t) const { std::stringstream stream; stream << +t; return stream.str(); @@ -1744,8 +2267,8 @@ namespace sqlite_orm { * char is neigher signer char nor unsigned char so it has its own specialization */ template<> - struct field_printer<char> { - std::string operator()(const char &t) const { + struct field_printer<char, void> { + std::string operator()(const char& t) const { std::stringstream stream; stream << +t; return stream.str(); @@ -1753,15 +2276,15 @@ namespace sqlite_orm { }; template<> - struct field_printer<std::string> { - std::string operator()(const std::string &t) const { + struct field_printer<std::string, void> { + std::string operator()(const std::string& t) const { return t; } }; template<> - struct field_printer<std::vector<char>> { - std::string operator()(const std::vector<char> &t) const { + struct field_printer<std::vector<char>, void> { + std::string operator()(const std::vector<char>& t) const { std::stringstream ss; ss << std::hex; for(auto c: t) { @@ -1772,15 +2295,22 @@ namespace sqlite_orm { }; template<> - struct field_printer<std::nullptr_t> { - std::string operator()(const std::nullptr_t &) const { + struct field_printer<std::nullptr_t, void> { + std::string operator()(const std::nullptr_t&) const { return "null"; } }; - +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct field_printer<std::nullopt_t, void> { + std::string operator()(const std::nullopt_t&) const { + return "null"; + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct field_printer<std::shared_ptr<T>> { - std::string operator()(const std::shared_ptr<T> &t) const { + struct field_printer<std::shared_ptr<T>, void> { + std::string operator()(const std::shared_ptr<T>& t) const { if(t) { return field_printer<T>()(*t); } else { @@ -1790,8 +2320,8 @@ namespace sqlite_orm { }; template<class T> - struct field_printer<std::unique_ptr<T>> { - std::string operator()(const std::unique_ptr<T> &t) const { + struct field_printer<std::unique_ptr<T>, void> { + std::string operator()(const std::unique_ptr<T>& t) const { if(t) { return field_printer<T>()(*t); } else { @@ -1802,8 +2332,8 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct field_printer<std::optional<T>> { - std::string operator()(const std::optional<T> &t) const { + struct field_printer<std::optional<T>, void> { + std::string operator()(const std::optional<T>& t) const { if(t.has_value()) { return field_printer<T>()(*t); } else { @@ -1818,7 +2348,8 @@ namespace sqlite_orm { #include <string> // std::string #include <type_traits> // std::enable_if, std::is_same #include <vector> // std::vector -#include <tuple> // std::tuple +#include <tuple> // std::tuple, std::tuple_size +#include <sstream> // std::stringstream // #include "collate_argument.h" @@ -1841,7 +2372,7 @@ namespace sqlite_orm { type field; template<class L> - void apply(const L &l) const { + void apply(const L& l) const { l(this->field); } }; @@ -1851,20 +2382,93 @@ namespace sqlite_orm { using type = void; template<class L> - void apply(const L &) const { + void apply(const L&) const { //.. } }; } } -// #include "negatable.h" +// #include "tags.h" + +// #include "expression.h" +// #include "operators.h" namespace sqlite_orm { namespace internal { - struct arithmetic_t; + + template<class L, class R> + struct and_condition_t; + + template<class L, class R> + struct or_condition_t; + + /** + * Is not an operator but a result of c(...) function. Has operator= overloaded which returns assign_t + */ + template<class T> + struct expression_t : condition_t { + T value; + + expression_t(T value_) : value(std::move(value_)) {} + + template<class R> + assign_t<T, R> operator=(R r) const { + return {this->value, std::move(r)}; + } + + assign_t<T, std::nullptr_t> operator=(std::nullptr_t) const { + return {this->value, nullptr}; + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + assign_t<T, std::nullopt_t> operator=(std::nullopt_t) const { + return {this->value, std::nullopt}; + } +#endif + template<class... Args> + in_t<T, Args...> in(Args... args) const { + return {this->value, std::make_tuple(std::forward<Args>(args)...), false}; + } + + template<class... Args> + in_t<T, Args...> not_in(Args... args) const { + return {this->value, std::make_tuple(std::forward<Args>(args)...), true}; + } + + template<class R> + and_condition_t<T, R> and_(R right) const { + return {this->value, std::move(right)}; + } + + template<class R> + or_condition_t<T, R> or_(R right) const { + return {this->value, std::move(right)}; + } + }; + + template<class T> + T get_from_expression(T value) { + return std::move(value); + } + + template<class T> + T get_from_expression(expression_t<T> expression) { + return std::move(expression.value); + } + } + + /** + * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or + * `storage.update(set(c(&User::name) = "Dua Lipa")); + */ + template<class T> + internal::expression_t<T> c(T value) { + return {std::move(value)}; } +} + +namespace sqlite_orm { namespace internal { @@ -1880,7 +2484,7 @@ namespace sqlite_orm { template<class T, bool has_offset, bool offset_is_implicit, class O> struct limit_t : limit_string { T lim; - internal::optional_container<O> off; + optional_container<O> off; limit_t() = default; @@ -1910,22 +2514,17 @@ namespace sqlite_orm { struct is_offset<offset_t<T>> : std::true_type {}; /** - * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators - */ - struct condition_t {}; - - /** * Collated something */ template<class T> struct collate_t : public condition_t { T expr; - internal::collate_argument argument; + collate_argument argument; - collate_t(T expr_, internal::collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} + collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} operator std::string() const { - return constraints::collate_t{this->argument}; + return collate_constraint_t{this->argument}; } }; @@ -1965,11 +2564,16 @@ namespace sqlite_orm { /** * Base class for binary conditions + * L is left argument type + * R is right argument type + * S is 'string' class (a class which has cast to `std::string` operator) + * Res is result type */ - template<class L, class R> - struct binary_condition : public condition_t { + template<class L, class R, class S, class Res> + struct binary_condition : condition_t, S { using left_type = L; using right_type = R; + using result_type = Res; left_type l; right_type r; @@ -1989,12 +2593,17 @@ namespace sqlite_orm { * Result of and operator */ template<class L, class R> - struct and_condition_t : binary_condition<L, R>, and_condition_string { - using super = binary_condition<L, R>; + struct and_condition_t : binary_condition<L, R, and_condition_string, bool> { + using super = binary_condition<L, R, and_condition_string, bool>; using super::super; }; + template<class L, class R> + and_condition_t<L, R> make_and_condition(L left, R right) { + return {std::move(left), std::move(right)}; + } + struct or_condition_string { operator std::string() const { return "OR"; @@ -2005,12 +2614,17 @@ namespace sqlite_orm { * Result of or operator */ template<class L, class R> - struct or_condition_t : binary_condition<L, R>, or_condition_string { - using super = binary_condition<L, R>; + struct or_condition_t : binary_condition<L, R, or_condition_string, bool> { + using super = binary_condition<L, R, or_condition_string, bool>; using super::super; }; + template<class L, class R> + or_condition_t<L, R> make_or_condition(L left, R right) { + return {std::move(left), std::move(right)}; + } + struct is_equal_string { operator std::string() const { return "="; @@ -2021,26 +2635,35 @@ namespace sqlite_orm { * = and == operators object */ template<class L, class R> - struct is_equal_t : binary_condition<L, R>, is_equal_string, internal::negatable_t { + struct is_equal_t : binary_condition<L, R, is_equal_string, bool>, negatable_t { using self = is_equal_t<L, R>; - using binary_condition<L, R>::binary_condition; + using binary_condition<L, R, is_equal_string, bool>::binary_condition; collate_t<self> collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t<self> collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t<self> collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } named_collate<self> collate(std::string name) const { return {*this, std::move(name)}; } + + template<class C> + named_collate<self> collate() const { + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + return {*this, std::move(name)}; + } }; struct is_not_equal_string { @@ -2053,21 +2676,21 @@ namespace sqlite_orm { * != operator object */ template<class L, class R> - struct is_not_equal_t : binary_condition<L, R>, is_not_equal_string, internal::negatable_t { + struct is_not_equal_t : binary_condition<L, R, is_not_equal_string, bool>, negatable_t { using self = is_not_equal_t<L, R>; - using binary_condition<L, R>::binary_condition; + using binary_condition<L, R, is_not_equal_string, bool>::binary_condition; collate_t<self> collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t<self> collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t<self> collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -2081,21 +2704,21 @@ namespace sqlite_orm { * > operator object. */ template<class L, class R> - struct greater_than_t : binary_condition<L, R>, greater_than_string, internal::negatable_t { + struct greater_than_t : binary_condition<L, R, greater_than_string, bool>, negatable_t { using self = greater_than_t<L, R>; - using binary_condition<L, R>::binary_condition; + using binary_condition<L, R, greater_than_string, bool>::binary_condition; collate_t<self> collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t<self> collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t<self> collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -2109,21 +2732,21 @@ namespace sqlite_orm { * >= operator object. */ template<class L, class R> - struct greater_or_equal_t : binary_condition<L, R>, greater_or_equal_string, internal::negatable_t { + struct greater_or_equal_t : binary_condition<L, R, greater_or_equal_string, bool>, negatable_t { using self = greater_or_equal_t<L, R>; - using binary_condition<L, R>::binary_condition; + using binary_condition<L, R, greater_or_equal_string, bool>::binary_condition; collate_t<self> collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t<self> collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t<self> collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -2137,21 +2760,21 @@ namespace sqlite_orm { * < operator object. */ template<class L, class R> - struct lesser_than_t : binary_condition<L, R>, lesser_than_string, internal::negatable_t { + struct lesser_than_t : binary_condition<L, R, lesser_than_string, bool>, negatable_t { using self = lesser_than_t<L, R>; - using binary_condition<L, R>::binary_condition; + using binary_condition<L, R, lesser_than_string, bool>::binary_condition; collate_t<self> collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t<self> collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t<self> collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -2165,21 +2788,21 @@ namespace sqlite_orm { * <= operator object. */ template<class L, class R> - struct lesser_or_equal_t : binary_condition<L, R>, lesser_or_equal_string, internal::negatable_t { + struct lesser_or_equal_t : binary_condition<L, R, lesser_or_equal_string, bool>, negatable_t { using self = lesser_or_equal_t<L, R>; - using binary_condition<L, R>::binary_condition; + using binary_condition<L, R, lesser_or_equal_string, bool>::binary_condition; collate_t<self> collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t<self> collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t<self> collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -2199,13 +2822,23 @@ namespace sqlite_orm { * IN operator object. */ template<class L, class A> - struct in_t : condition_t, in_base, internal::negatable_t { - using self = in_t<L, A>; + struct dynamic_in_t : condition_t, in_base, negatable_t { + using self = dynamic_in_t<L, A>; - L l; // left expression - A arg; // in arg + L left; // left expression + A argument; // in arg - in_t(L l_, A arg_, bool negative_) : in_base{negative_}, l(l_), arg(std::move(arg_)) {} + dynamic_in_t(L left_, A argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; + + template<class L, class... Args> + struct in_t : condition_t, in_base, negatable_t { + L left; + std::tuple<Args...> argument; + + in_t(L left_, decltype(argument) argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} }; struct is_null_string { @@ -2218,7 +2851,7 @@ namespace sqlite_orm { * IS NULL operator object. */ template<class T> - struct is_null_t : is_null_string, internal::negatable_t { + struct is_null_t : is_null_string, negatable_t { using self = is_null_t<T>; T t; @@ -2236,7 +2869,7 @@ namespace sqlite_orm { * IS NOT NULL operator object. */ template<class T> - struct is_not_null_t : is_not_null_string, internal::negatable_t { + struct is_not_null_t : is_not_null_string, negatable_t { using self = is_not_null_t<T>; T t; @@ -2244,32 +2877,14 @@ namespace sqlite_orm { is_not_null_t(T t_) : t(std::move(t_)) {} }; - struct where_string { - operator std::string() const { - return "WHERE"; - } - }; - - /** - * WHERE argument holder. - * C is conditions type. Can be any condition like: is_equal_t, is_null_t, exists_t etc - */ - template<class C> - struct where_t : where_string { - C c; - - where_t(C c_) : c(std::move(c_)) {} - }; - - template<class T> - struct is_where : std::false_type {}; - - template<class T> - struct is_where<where_t<T>> : std::true_type {}; - struct order_by_base { int asc_desc = 0; // 1: asc, -1: desc std::string _collate_argument; + + order_by_base() = default; + + order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : + asc_desc(asc_desc_), _collate_argument(move(_collate_argument_)) {} }; struct order_by_string { @@ -2283,11 +2898,12 @@ namespace sqlite_orm { */ template<class O> struct order_by_t : order_by_base, order_by_string { - using self = order_by_t<O>; + using expression_type = O; + using self = order_by_t<expression_type>; - O o; + expression_type expression; - order_by_t(O o_) : o(std::move(o_)) {} + order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} self asc() { auto res = *this; @@ -2303,22 +2919,22 @@ namespace sqlite_orm { self collate_binary() const { auto res = *this; - res._collate_argument = constraints::collate_t::string_from_collate_argument( - sqlite_orm::internal::collate_argument::binary); + res._collate_argument = + collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::binary); return res; } self collate_nocase() const { auto res = *this; - res._collate_argument = constraints::collate_t::string_from_collate_argument( - sqlite_orm::internal::collate_argument::nocase); + res._collate_argument = + collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::nocase); return res; } self collate_rtrim() const { auto res = *this; res._collate_argument = - constraints::collate_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::rtrim); + collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::rtrim); return res; } @@ -2327,6 +2943,15 @@ namespace sqlite_orm { res._collate_argument = std::move(name); return res; } + + template<class C> + self collate() const { + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + return this->collate(move(name)); + } }; /** @@ -2338,7 +2963,7 @@ namespace sqlite_orm { args_type args; - multi_order_by_t(args_type &&args_) : args(std::move(args_)) {} + multi_order_by_t(args_type&& args_) : args(std::move(args_)) {} }; struct dynamic_order_by_entry_t : order_by_base { @@ -2357,13 +2982,13 @@ namespace sqlite_orm { using entry_t = dynamic_order_by_entry_t; using const_iterator = typename std::vector<entry_t>::const_iterator; - dynamic_order_by_t(const context_t &context_) : context(context_) {} + dynamic_order_by_t(const context_t& context_) : context(context_) {} template<class O> void push_back(order_by_t<O> order_by) { auto newContext = this->context; newContext.skip_table_name = true; - auto columnName = serialize(order_by.o, newContext); + auto columnName = serialize(order_by.expression, newContext); entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument)); } @@ -2410,7 +3035,7 @@ namespace sqlite_orm { using args_type = std::tuple<Args...>; args_type args; - group_by_t(args_type &&args_) : args(std::move(args_)) {} + group_by_t(args_type&& args_) : args(std::move(args_)) {} }; template<class T> @@ -2452,7 +3077,7 @@ namespace sqlite_orm { * LIKE operator object. */ template<class A, class T, class E> - struct like_t : condition_t, like_string, internal::negatable_t { + struct like_t : condition_t, like_string, negatable_t { using self = like_t<A, T, E>; using arg_t = A; using pattern_t = T; @@ -2460,15 +3085,14 @@ namespace sqlite_orm { arg_t arg; pattern_t pattern; - sqlite_orm::internal::optional_container<escape_t> - arg3; // not escape cause escape exists as a function here + optional_container<escape_t> arg3; // not escape cause escape exists as a function here - like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container<escape_t> escape_) : + like_t(arg_t arg_, pattern_t pattern_, optional_container<escape_t> escape_) : arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} template<class C> like_t<A, T, C> escape(C c) const { - sqlite_orm::internal::optional_container<C> newArg3{std::move(c)}; + optional_container<C> newArg3{std::move(c)}; return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; } }; @@ -2699,6 +3323,26 @@ namespace sqlite_orm { cast_t(expression_type expression_) : expression(std::move(expression_)) {} }; + template<class... Args> + struct from_t { + using tuple_type = std::tuple<Args...>; + }; + + template<class T> + struct is_from : std::false_type {}; + + template<class... Args> + struct is_from<from_t<Args...>> : std::true_type {}; + } + + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from<User>());` + */ + template<class... Args> + internal::from_t<Args...> from() { + static_assert(std::tuple_size<std::tuple<Args...>>::value > 0, ""); + return {}; } template<class T, typename = typename std::enable_if<std::is_base_of<internal::negatable_t, T>::value>::type> @@ -2711,152 +3355,152 @@ namespace sqlite_orm { */ template<class T, class R> internal::lesser_than_t<T, R> operator<(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::lesser_than_t<L, T> operator<(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class T, class R> internal::lesser_or_equal_t<T, R> operator<=(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::lesser_or_equal_t<L, T> operator<=(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class T, class R> internal::greater_than_t<T, R> operator>(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::greater_than_t<L, T> operator>(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class T, class R> internal::greater_or_equal_t<T, R> operator>=(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::greater_or_equal_t<L, T> operator>=(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class T, class R> internal::is_equal_t<T, R> operator==(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::is_equal_t<L, T> operator==(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class T, class R> internal::is_not_equal_t<T, R> operator!=(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::is_not_equal_t<L, T> operator!=(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class T, class R> internal::conc_t<T, R> operator||(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::conc_t<L, T> operator||(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class L, class R> internal::conc_t<L, R> operator||(internal::expression_t<L> l, internal::expression_t<R> r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template<class T, class R> internal::add_t<T, R> operator+(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::add_t<L, T> operator+(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class L, class R> internal::add_t<L, R> operator+(internal::expression_t<L> l, internal::expression_t<R> r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template<class T, class R> internal::sub_t<T, R> operator-(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::sub_t<L, T> operator-(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class L, class R> internal::sub_t<L, R> operator-(internal::expression_t<L> l, internal::expression_t<R> r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template<class T, class R> internal::mul_t<T, R> operator*(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::mul_t<L, T> operator*(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class L, class R> internal::mul_t<L, R> operator*(internal::expression_t<L> l, internal::expression_t<R> r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template<class T, class R> internal::div_t<T, R> operator/(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::div_t<L, T> operator/(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class L, class R> internal::div_t<L, R> operator/(internal::expression_t<L> l, internal::expression_t<R> r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template<class T, class R> internal::mod_t<T, R> operator%(internal::expression_t<T> expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template<class L, class T> internal::mod_t<L, T> operator%(L l, internal::expression_t<T> expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template<class L, class R> internal::mod_t<L, R> operator%(internal::expression_t<L> l, internal::expression_t<R> r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template<class F, class O> @@ -2924,16 +3568,30 @@ namespace sqlite_orm { class R, typename = typename std::enable_if<std::is_base_of<internal::condition_t, L>::value || std::is_base_of<internal::condition_t, R>::value>::type> - internal::and_condition_t<L, R> operator&&(L l, R r) { - return {std::move(l), std::move(r)}; + auto operator&&(L l, R r) { + using internal::get_from_expression; + return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); + } + + template<class L, class R> + auto and_(L l, R r) { + using internal::get_from_expression; + return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); } template<class L, class R, typename = typename std::enable_if<std::is_base_of<internal::condition_t, L>::value || std::is_base_of<internal::condition_t, R>::value>::type> - internal::or_condition_t<L, R> operator||(L l, R r) { - return {std::move(l), std::move(r)}; + auto operator||(L l, R r) { + using internal::get_from_expression; + return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); + } + + template<class L, class R> + auto or_(L l, R r) { + using internal::get_from_expression; + return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); } template<class T> @@ -2947,32 +3605,32 @@ namespace sqlite_orm { } template<class L, class E> - internal::in_t<L, std::vector<E>> in(L l, std::vector<E> values) { + internal::dynamic_in_t<L, std::vector<E>> in(L l, std::vector<E> values) { return {std::move(l), std::move(values), false}; } template<class L, class E> - internal::in_t<L, std::vector<E>> in(L l, std::initializer_list<E> values) { + internal::dynamic_in_t<L, std::vector<E>> in(L l, std::initializer_list<E> values) { return {std::move(l), std::move(values), false}; } template<class L, class A> - internal::in_t<L, A> in(L l, A arg) { + internal::dynamic_in_t<L, A> in(L l, A arg) { return {std::move(l), std::move(arg), false}; } template<class L, class E> - internal::in_t<L, std::vector<E>> not_in(L l, std::vector<E> values) { + internal::dynamic_in_t<L, std::vector<E>> not_in(L l, std::vector<E> values) { return {std::move(l), std::move(values), true}; } template<class L, class E> - internal::in_t<L, std::vector<E>> not_in(L l, std::initializer_list<E> values) { + internal::dynamic_in_t<L, std::vector<E>> not_in(L l, std::initializer_list<E> values) { return {std::move(l), std::move(values), true}; } template<class L, class A> - internal::in_t<L, A> not_in(L l, A arg) { + internal::dynamic_in_t<L, A> not_in(L l, A arg) { return {std::move(l), std::move(arg), true}; } @@ -3036,11 +3694,6 @@ namespace sqlite_orm { return {std::move(l), std::move(r)}; } - template<class C> - internal::where_t<C> where(C c) { - return {std::move(c)}; - } - /** * ORDER BY column * Example: storage.select(&User::name, order_by(&User::id)) @@ -3055,7 +3708,7 @@ namespace sqlite_orm { * Example: storage.get_all<Singer>(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) */ template<class... Args> - internal::multi_order_by_t<Args...> multi_order_by(Args &&... args) { + internal::multi_order_by_t<Args...> multi_order_by(Args&&... args) { return {std::make_tuple(std::forward<Args>(args)...)}; } @@ -3073,7 +3726,7 @@ namespace sqlite_orm { */ template<class S> internal::dynamic_order_by_t<internal::serializator_context<typename S::impl_type>> - dynamic_order_by(const S &storage) { + dynamic_order_by(const S& storage) { internal::serializator_context_builder<S> builder(storage); return builder(); } @@ -3083,7 +3736,7 @@ namespace sqlite_orm { * Example: storage.get_all<Employee>(group_by(&Employee::name)) */ template<class... Args> - internal::group_by_t<Args...> group_by(Args &&... args) { + internal::group_by_t<Args...> group_by(Args&&... args) { return {std::make_tuple(std::forward<Args>(args)...)}; } @@ -3195,7 +3848,7 @@ namespace sqlite_orm { alias_column_t(){}; - alias_column_t(column_type column_) : column(column_) {} + alias_column_t(column_type column_) : column(std::move(column_)) {} }; template<class T, class SFINAE = void> @@ -3217,6 +3870,9 @@ namespace sqlite_orm { } }; + /** + * Used to store alias for expression + */ template<class T, class E> struct as_t { using alias_type = T; @@ -3314,19 +3970,13 @@ namespace sqlite_orm { namespace internal { template<class... Args> - struct join_iterator { - - template<class L> - void operator()(const L &) { - //.. - } - }; + struct join_iterator; template<> struct join_iterator<> { template<class L> - void operator()(const L &) { + void operator()(const L&) const { //.. } }; @@ -3336,7 +3986,7 @@ namespace sqlite_orm { using super = join_iterator<Tail...>; template<class L> - void operator()(const L &l) { + void operator()(const L& l) const { this->super::operator()(l); } }; @@ -3347,7 +3997,7 @@ namespace sqlite_orm { using join_type = cross_join_t<T>; template<class L> - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -3359,7 +4009,7 @@ namespace sqlite_orm { using join_type = natural_join_t<T>; template<class L> - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -3371,7 +4021,7 @@ namespace sqlite_orm { using join_type = left_join_t<T, O>; template<class L> - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -3383,7 +4033,7 @@ namespace sqlite_orm { using join_type = join_t<T, O>; template<class L> - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -3395,7 +4045,7 @@ namespace sqlite_orm { using join_type = left_outer_join_t<T, O>; template<class L> - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -3407,7 +4057,7 @@ namespace sqlite_orm { using join_type = inner_join_t<T, O>; template<class L> - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -3442,11 +4092,11 @@ namespace sqlite_orm { template<template<typename...> class Base, typename Derived> struct is_base_of_template_impl { template<typename... Ts> - static constexpr std::true_type test(const Base<Ts...> *); + static constexpr std::true_type test(const Base<Ts...>*); static constexpr std::false_type test(...); - using type = decltype(test(std::declval<Derived *>())); + using type = decltype(test(std::declval<Derived*>())); }; template<typename Derived, template<typename...> class Base> @@ -3454,22 +4104,46 @@ namespace sqlite_orm { #else template<template<typename...> class C, typename... Ts> - std::true_type is_base_of_template_impl(const C<Ts...> *); + std::true_type is_base_of_template_impl(const C<Ts...>*); template<template<typename...> class C> std::false_type is_base_of_template_impl(...); template<typename T, template<typename...> class C> - using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T *>())); + using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T*>())); +#endif + } +} + +// #include "serialize_result_type.h" + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include <string_view> // string_view +#else +#include <string> // std::string +#endif + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; +#else + using serialize_result_type = std::string; #endif } } namespace sqlite_orm { + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + namespace internal { template<class T> + struct is_into; + + template<class T> struct unique_ptr_result_of {}; /** @@ -3479,7 +4153,7 @@ namespace sqlite_orm { * Args - function arguments types */ template<class R, class S, class... Args> - struct core_function_t : S, internal::arithmetic_t { + struct built_in_function_t : S, arithmetic_t { using return_type = R; using string_type = S; using args_type = std::tuple<Args...>; @@ -3488,89 +4162,113 @@ namespace sqlite_orm { args_type args; - core_function_t(args_type &&args_) : args(std::move(args_)) {} + built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + }; + + struct typeof_string { + serialize_result_type serialize() const { + return "TYPEOF"; + } + }; + + struct unicode_string { + serialize_result_type serialize() const { + return "UNICODE"; + } }; struct length_string { - operator std::string() const { + serialize_result_type serialize() const { return "LENGTH"; } }; struct abs_string { - operator std::string() const { + serialize_result_type serialize() const { return "ABS"; } }; struct lower_string { - operator std::string() const { + serialize_result_type serialize() const { return "LOWER"; } }; struct upper_string { - operator std::string() const { + serialize_result_type serialize() const { return "UPPER"; } }; + struct last_insert_rowid_string { + serialize_result_type serialize() const { + return "LAST_INSERT_ROWID"; + } + }; + + struct total_changes_string { + serialize_result_type serialize() const { + return "TOTAL_CHANGES"; + } + }; + struct changes_string { - operator std::string() const { + serialize_result_type serialize() const { return "CHANGES"; } }; struct trim_string { - operator std::string() const { + serialize_result_type serialize() const { return "TRIM"; } }; struct ltrim_string { - operator std::string() const { + serialize_result_type serialize() const { return "LTRIM"; } }; struct rtrim_string { - operator std::string() const { + serialize_result_type serialize() const { return "RTRIM"; } }; struct hex_string { - operator std::string() const { + serialize_result_type serialize() const { return "HEX"; } }; struct quote_string { - operator std::string() const { + serialize_result_type serialize() const { return "QUOTE"; } }; struct randomblob_string { - operator std::string() const { + serialize_result_type serialize() const { return "RANDOMBLOB"; } }; struct instr_string { - operator std::string() const { + serialize_result_type serialize() const { return "INSTR"; } }; struct replace_string { - operator std::string() const { + serialize_result_type serialize() const { return "REPLACE"; } }; struct round_string { - operator std::string() const { + serialize_result_type serialize() const { return "ROUND"; } }; @@ -3578,13 +4276,13 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3007016 struct char_string { - operator std::string() const { + serialize_result_type serialize() const { return "CHAR"; } }; struct random_string { - operator std::string() const { + serialize_result_type serialize() const { return "RANDOM"; } }; @@ -3592,73 +4290,79 @@ namespace sqlite_orm { #endif struct coalesce_string { - operator std::string() const { + serialize_result_type serialize() const { return "COALESCE"; } }; + struct ifnull_string { + serialize_result_type serialize() const { + return "IFNULL"; + } + }; + struct date_string { - operator std::string() const { + serialize_result_type serialize() const { return "DATE"; } }; struct time_string { - operator std::string() const { + serialize_result_type serialize() const { return "TIME"; } }; struct datetime_string { - operator std::string() const { + serialize_result_type serialize() const { return "DATETIME"; } }; struct julianday_string { - operator std::string() const { + serialize_result_type serialize() const { return "JULIANDAY"; } }; struct strftime_string { - operator std::string() const { + serialize_result_type serialize() const { return "STRFTIME"; } }; struct zeroblob_string { - operator std::string() const { + serialize_result_type serialize() const { return "ZEROBLOB"; } }; struct substr_string { - operator std::string() const { + serialize_result_type serialize() const { return "SUBSTR"; } }; #ifdef SQLITE_SOUNDEX struct soundex_string { - operator std::string() const { + serialize_result_type serialize() const { return "SOUNDEX"; } }; #endif struct total_string { - operator std::string() const { + serialize_result_type serialize() const { return "TOTAL"; } }; struct sum_string { - operator std::string() const { + serialize_result_type serialize() const { return "SUM"; } }; struct count_string { - operator std::string() const { + serialize_result_type serialize() const { return "COUNT"; } }; @@ -3696,123 +4400,1254 @@ namespace sqlite_orm { struct count_asterisk_without_type : count_string {}; struct avg_string { - operator std::string() const { + serialize_result_type serialize() const { return "AVG"; } }; struct max_string { - operator std::string() const { + serialize_result_type serialize() const { return "MAX"; } }; struct min_string { - operator std::string() const { + serialize_result_type serialize() const { return "MIN"; } }; struct group_concat_string { - operator std::string() const { + serialize_result_type serialize() const { return "GROUP_CONCAT"; } }; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + struct acos_string { + serialize_result_type serialize() const { + return "ACOS"; + } + }; + + struct acosh_string { + serialize_result_type serialize() const { + return "ACOSH"; + } + }; + + struct asin_string { + serialize_result_type serialize() const { + return "ASIN"; + } + }; + + struct asinh_string { + serialize_result_type serialize() const { + return "ASINH"; + } + }; + + struct atan_string { + serialize_result_type serialize() const { + return "ATAN"; + } + }; + + struct atan2_string { + serialize_result_type serialize() const { + return "ATAN2"; + } + }; + + struct atanh_string { + serialize_result_type serialize() const { + return "ATANH"; + } + }; + + struct ceil_string { + serialize_result_type serialize() const { + return "CEIL"; + } + }; + struct ceiling_string { + serialize_result_type serialize() const { + return "CEILING"; + } + }; + + struct cos_string { + serialize_result_type serialize() const { + return "COS"; + } + }; + + struct cosh_string { + serialize_result_type serialize() const { + return "COSH"; + } + }; + + struct degrees_string { + serialize_result_type serialize() const { + return "DEGREES"; + } + }; + + struct exp_string { + serialize_result_type serialize() const { + return "EXP"; + } + }; + + struct floor_string { + serialize_result_type serialize() const { + return "FLOOR"; + } + }; + + struct ln_string { + serialize_result_type serialize() const { + return "LN"; + } + }; + + struct log_string { + serialize_result_type serialize() const { + return "LOG"; + } + }; + + struct log10_string { + serialize_result_type serialize() const { + return "LOG10"; + } + }; + + struct log2_string { + serialize_result_type serialize() const { + return "LOG2"; + } + }; + + struct mod_string { + serialize_result_type serialize() const { + return "MOD"; + } + }; + + struct pi_string { + serialize_result_type serialize() const { + return "PI"; + } + }; + + struct pow_string { + serialize_result_type serialize() const { + return "POW"; + } + }; + + struct power_string { + serialize_result_type serialize() const { + return "POWER"; + } + }; + + struct radians_string { + serialize_result_type serialize() const { + return "RADIANS"; + } + }; + + struct sin_string { + serialize_result_type serialize() const { + return "SIN"; + } + }; + + struct sinh_string { + serialize_result_type serialize() const { + return "SINH"; + } + }; + + struct sqrt_string { + serialize_result_type serialize() const { + return "SQRT"; + } + }; + + struct tan_string { + serialize_result_type serialize() const { + return "TAN"; + } + }; + + struct tanh_string { + serialize_result_type serialize() const { + return "TANH"; + } + }; + + struct trunc_string { + serialize_result_type serialize() const { + return "TRUNC"; + } + }; + +#endif // SQLITE_ENABLE_MATH_FUNCTIONS +#ifdef SQLITE_ENABLE_JSON1 + struct json_string { + serialize_result_type serialize() const { + return "JSON"; + } + }; + + struct json_array_string { + serialize_result_type serialize() const { + return "JSON_ARRAY"; + } + }; + + struct json_array_length_string { + serialize_result_type serialize() const { + return "JSON_ARRAY_LENGTH"; + } + }; + + struct json_extract_string { + serialize_result_type serialize() const { + return "JSON_EXTRACT"; + } + }; + + struct json_insert_string { + serialize_result_type serialize() const { + return "JSON_INSERT"; + } + }; + + struct json_replace_string { + serialize_result_type serialize() const { + return "JSON_REPLACE"; + } + }; + + struct json_set_string { + serialize_result_type serialize() const { + return "JSON_SET"; + } + }; + + struct json_object_string { + serialize_result_type serialize() const { + return "JSON_OBJECT"; + } + }; + + struct json_patch_string { + serialize_result_type serialize() const { + return "JSON_PATCH"; + } + }; + + struct json_remove_string { + serialize_result_type serialize() const { + return "JSON_REMOVE"; + } + }; + + struct json_type_string { + serialize_result_type serialize() const { + return "JSON_TYPE"; + } + }; + + struct json_valid_string { + serialize_result_type serialize() const { + return "JSON_VALID"; + } + }; + + struct json_quote_string { + serialize_result_type serialize() const { + return "JSON_QUOTE"; + } + }; + + struct json_group_array_string { + serialize_result_type serialize() const { + return "JSON_GROUP_ARRAY"; + } + }; + + struct json_group_object_string { + serialize_result_type serialize() const { + return "JSON_GROUP_OBJECT"; + } + }; +#endif // SQLITE_ENABLE_JSON1 } /** * Cute operators for core functions */ - - template< - class F, - class R, - typename = typename std::enable_if<internal::is_base_of_template<F, internal::core_function_t>::value>::type> + template<class F, + class R, + typename = + typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> internal::lesser_than_t<F, R> operator<(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if<internal::is_base_of_template<F, internal::core_function_t>::value>::type> + template<class F, + class R, + typename = + typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> internal::lesser_or_equal_t<F, R> operator<=(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if<internal::is_base_of_template<F, internal::core_function_t>::value>::type> + template<class F, + class R, + typename = + typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> internal::greater_than_t<F, R> operator>(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if<internal::is_base_of_template<F, internal::core_function_t>::value>::type> + template<class F, + class R, + typename = + typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> internal::greater_or_equal_t<F, R> operator>=(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if<internal::is_base_of_template<F, internal::core_function_t>::value>::type> + template<class F, + class R, + typename = + typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> internal::is_equal_t<F, R> operator==(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if<internal::is_base_of_template<F, internal::core_function_t>::value>::type> + template<class F, + class R, + typename = + typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> internal::is_not_equal_t<F, R> operator!=(F f, R r) { return {std::move(f), std::move(r)}; } +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos(&Triangle::cornerA)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::acos_string, X> acos(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos<std::optional<double>>(&Triangle::cornerA)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::acos_string, X> acos(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh(&Triangle::cornerA)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::acosh_string, X> acosh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh<std::optional<double>>(&Triangle::cornerA)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::acosh_string, X> acosh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin(&Triangle::cornerA)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::asin_string, X> asin(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin<std::optional<double>>(&Triangle::cornerA)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::asin_string, X> asin(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh(&Triangle::cornerA)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::asinh_string, X> asinh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh<std::optional<double>>(&Triangle::cornerA)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::asinh_string, X> asinh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan(1)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::atan_string, X> atan(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan<std::optional<double>>(1)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::atan_string, X> atan(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2(1, 3)); // decltype(rows) is std::vector<double> + */ + template<class X, class Y> + internal::built_in_function_t<double, internal::atan2_string, X, Y> atan2(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2<std::optional<double>>(1, 3)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X, class Y> + internal::built_in_function_t<R, internal::atan2_string, X, Y> atan2(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh(1)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::atanh_string, X> atanh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh<std::optional<double>>(1)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::atanh_string, X> atanh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil(&User::rating)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::ceil_string, X> ceil(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil<std::optional<double>>(&User::rating)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::ceil_string, X> ceil(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling(&User::rating)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::ceiling_string, X> ceiling(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling<std::optional<double>>(&User::rating)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::ceiling_string, X> ceiling(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos(&Triangle::cornerB)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::cos_string, X> cos(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos<std::optional<double>>(&User::rating)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::cos_string, X> cos(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh(&Triangle::cornerB)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::cosh_string, X> cosh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh<std::optional<double>>(&User::rating)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::cosh_string, X> cosh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees(&Triangle::cornerB)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::degrees_string, X> degrees(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees<std::optional<double>>(&User::rating)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::degrees_string, X> degrees(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp(&Triangle::cornerB)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::exp_string, X> exp(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp<std::optional<double>>(&User::rating)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::exp_string, X> exp(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor(&User::rating)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::floor_string, X> floor(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor<std::optional<double>>(&User::rating)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::floor_string, X> floor(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln(200)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::ln_string, X> ln(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln<std::optional<double>>(200)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::ln_string, X> ln(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(100)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::log_string, X> log(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log<std::optional<double>>(100)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::log_string, X> log(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10(100)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::log10_string, X> log10(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10<std::optional<double>>(100)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::log10_string, X> log10(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(10, 100)); // decltype(rows) is std::vector<double> + */ + template<class B, class X> + internal::built_in_function_t<double, internal::log_string, B, X> log(B b, X x) { + return {std::tuple<B, X>{std::forward<B>(b), std::forward<X>(x)}}; + } + + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log<std::optional<double>>(10, 100)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class B, class X> + internal::built_in_function_t<R, internal::log_string, B, X> log(B b, X x) { + return {std::tuple<B, X>{std::forward<B>(b), std::forward<X>(x)}}; + } + + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2(64)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::log2_string, X> log2(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2<std::optional<double>>(64)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::log2_string, X> log2(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f(6, 5)); // decltype(rows) is std::vector<double> + */ + template<class X, class Y> + internal::built_in_function_t<double, internal::mod_string, X, Y> mod_f(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f<std::optional<double>>(6, 5)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X, class Y> + internal::built_in_function_t<R, internal::mod_string, X, Y> mod_f(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector<double> + */ + inline internal::built_in_function_t<double, internal::pi_string> pi() { + return {{}}; + } + + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, etc. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi<float>()); // decltype(rows) is std::vector<float> + */ + template<class R> + internal::built_in_function_t<R, internal::pi_string> pi() { + return {{}}; + } + + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow(2, 5)); // decltype(rows) is std::vector<double> + */ + template<class X, class Y> + internal::built_in_function_t<double, internal::pow_string, X, Y> pow(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow<std::optional<double>>(2, 5)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X, class Y> + internal::built_in_function_t<R, internal::pow_string, X, Y> pow(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power(2, 5)); // decltype(rows) is std::vector<double> + */ + template<class X, class Y> + internal::built_in_function_t<double, internal::power_string, X, Y> power(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power<std::optional<double>>(2, 5)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X, class Y> + internal::built_in_function_t<R, internal::power_string, X, Y> power(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::radians_string, X> radians(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians<std::optional<double>>(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::radians_string, X> radians(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin(&Triangle::cornerA)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::sin_string, X> sin(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin<std::optional<double>>(&Triangle::cornerA)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::sin_string, X> sin(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh(&Triangle::cornerA)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::sinh_string, X> sinh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh<std::optional<double>>(&Triangle::cornerA)); // decltype(rows) is std::vector<std::optional<double>> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::sinh_string, X> sinh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::sqrt_string, X> sqrt(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt<int>(25)); // decltype(rows) is std::vector<int> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::sqrt_string, X> sqrt(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::tan_string, X> tan(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan<float>(&Triangle::cornerC)); // decltype(rows) is std::vector<float> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::tan_string, X> tan(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::tanh_string, X> tanh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh<float>(&Triangle::cornerC)); // decltype(rows) is std::vector<float> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::tanh_string, X> tanh(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector<double> + */ + template<class X> + internal::built_in_function_t<double, internal::trunc_string, X> trunc(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional<double>` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc<float>(5.5)); // decltype(rows) is std::vector<float> + */ + template<class R, class X> + internal::built_in_function_t<R, internal::trunc_string, X> trunc(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } +#endif // SQLITE_ENABLE_MATH_FUNCTIONS + /** + * TYPEOF(x) function https://sqlite.org/lang_corefunc.html#typeof + */ + template<class T> + internal::built_in_function_t<std::string, internal::typeof_string, T> typeof_(T t) { + return {std::tuple<T>{std::forward<T>(t)}}; + } + + /** + * UNICODE(x) function https://sqlite.org/lang_corefunc.html#unicode + */ + template<class T> + internal::built_in_function_t<int, internal::unicode_string, T> unicode(T t) { + return {std::tuple<T>{std::forward<T>(t)}}; + } /** * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length */ template<class T> - internal::core_function_t<int, internal::length_string, T> length(T t) { - std::tuple<T> args{std::forward<T>(t)}; - return {move(args)}; + internal::built_in_function_t<int, internal::length_string, T> length(T t) { + return {std::tuple<T>{std::forward<T>(t)}}; } /** * ABS(x) function https://sqlite.org/lang_corefunc.html#abs */ template<class T> - internal::core_function_t<std::unique_ptr<double>, internal::abs_string, T> abs(T t) { - std::tuple<T> args{std::forward<T>(t)}; - return {move(args)}; + internal::built_in_function_t<std::unique_ptr<double>, internal::abs_string, T> abs(T t) { + return {std::tuple<T>{std::forward<T>(t)}}; } /** * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower */ template<class T> - internal::core_function_t<std::string, internal::lower_string, T> lower(T t) { - std::tuple<T> args{std::forward<T>(t)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::lower_string, T> lower(T t) { + return {std::tuple<T>{std::forward<T>(t)}}; } /** * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper */ template<class T> - internal::core_function_t<std::string, internal::upper_string, T> upper(T t) { - std::tuple<T> args{std::forward<T>(t)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::upper_string, T> upper(T t) { + return {std::tuple<T>{std::forward<T>(t)}}; + } + + /** + * LAST_INSERT_ROWID(x) function https://www.sqlite.org/lang_corefunc.html#last_insert_rowid + */ + inline internal::built_in_function_t<int64, internal::last_insert_rowid_string> last_insert_rowid() { + return {{}}; + } + + /** + * TOTAL_CHANGES() function https://sqlite.org/lang_corefunc.html#total_changes + */ + inline internal::built_in_function_t<int, internal::total_changes_string> total_changes() { + return {{}}; } /** * CHANGES() function https://sqlite.org/lang_corefunc.html#changes */ - inline internal::core_function_t<int, internal::changes_string> changes() { + inline internal::built_in_function_t<int, internal::changes_string> changes() { return {{}}; } @@ -3820,117 +5655,106 @@ namespace sqlite_orm { * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim */ template<class T> - internal::core_function_t<std::string, internal::trim_string, T> trim(T t) { - std::tuple<T> args{std::forward<T>(t)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::trim_string, T> trim(T t) { + return {std::tuple<T>{std::forward<T>(t)}}; } /** * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim */ template<class X, class Y> - internal::core_function_t<std::string, internal::trim_string, X, Y> trim(X x, Y y) { - std::tuple<X, Y> args{std::forward<X>(x), std::forward<Y>(y)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::trim_string, X, Y> trim(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; } /** * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim */ template<class X> - internal::core_function_t<std::string, internal::ltrim_string, X> ltrim(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::ltrim_string, X> ltrim(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim */ template<class X, class Y> - internal::core_function_t<std::string, internal::ltrim_string, X, Y> ltrim(X x, Y y) { - std::tuple<X, Y> args{std::forward<X>(x), std::forward<Y>(y)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::ltrim_string, X, Y> ltrim(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; } /** * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim */ template<class X> - internal::core_function_t<std::string, internal::rtrim_string, X> rtrim(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::rtrim_string, X> rtrim(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim */ template<class X, class Y> - internal::core_function_t<std::string, internal::rtrim_string, X, Y> rtrim(X x, Y y) { - std::tuple<X, Y> args{std::forward<X>(x), std::forward<Y>(y)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::rtrim_string, X, Y> rtrim(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; } /** * HEX(X) function https://sqlite.org/lang_corefunc.html#hex */ template<class X> - internal::core_function_t<std::string, internal::hex_string, X> hex(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::hex_string, X> hex(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote */ template<class X> - internal::core_function_t<std::string, internal::quote_string, X> quote(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::quote_string, X> quote(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob */ template<class X> - internal::core_function_t<std::vector<char>, internal::randomblob_string, X> randomblob(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<std::vector<char>, internal::randomblob_string, X> randomblob(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr */ template<class X, class Y> - internal::core_function_t<int, internal::instr_string, X, Y> instr(X x, Y y) { - std::tuple<X, Y> args{std::forward<X>(x), std::forward<Y>(y)}; - return {move(args)}; + internal::built_in_function_t<int, internal::instr_string, X, Y> instr(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; } /** * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace */ template<class X, class Y, class Z> - internal::core_function_t<std::string, internal::replace_string, X, Y, Z> replace(X x, Y y, Z z) { - std::tuple<X, Y, Z> args{std::forward<X>(x), std::forward<Y>(y), std::forward<Z>(z)}; - return {move(args)}; + typename std::enable_if<internal::count_tuple<std::tuple<X, Y, Z>, internal::is_into>::value == 0, + internal::built_in_function_t<std::string, internal::replace_string, X, Y, Z>>::type + replace(X x, Y y, Z z) { + return {std::tuple<X, Y, Z>{std::forward<X>(x), std::forward<Y>(y), std::forward<Z>(z)}}; } /** * ROUND(X) function https://sqlite.org/lang_corefunc.html#round */ template<class X> - internal::core_function_t<double, internal::round_string, X> round(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<double, internal::round_string, X> round(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round */ template<class X, class Y> - internal::core_function_t<double, internal::round_string, X, Y> round(X x, Y y) { - std::tuple<X, Y> args{std::forward<X>(x), std::forward<Y>(y)}; - return {move(args)}; + internal::built_in_function_t<double, internal::round_string, X, Y> round(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; } #if SQLITE_VERSION_NUMBER >= 3007016 @@ -3939,14 +5763,14 @@ namespace sqlite_orm { * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char */ template<class... Args> - internal::core_function_t<std::string, internal::char_string, Args...> char_(Args... args) { + internal::built_in_function_t<std::string, internal::char_string, Args...> char_(Args... args) { return {std::make_tuple(std::forward<Args>(args)...)}; } /** * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random */ - inline internal::core_function_t<int, internal::random_string> random() { + inline internal::built_in_function_t<int, internal::random_string> random() { return {{}}; } @@ -3956,80 +5780,80 @@ namespace sqlite_orm { * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce */ template<class R, class... Args> - internal::core_function_t<R, internal::coalesce_string, Args...> coalesce(Args... args) { + internal::built_in_function_t<R, internal::coalesce_string, Args...> coalesce(Args... args) { return {std::make_tuple(std::forward<Args>(args)...)}; } /** + * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull + */ + template<class R, class X, class Y> + internal::built_in_function_t<R, internal::ifnull_string, X, Y> ifnull(X x, Y y) { + return {std::make_tuple(std::move(x), std::move(y))}; + } + + /** * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template<class... Args> - internal::core_function_t<std::string, internal::date_string, Args...> date(Args... args) { - std::tuple<Args...> t{std::forward<Args>(args)...}; - return {move(t)}; + internal::built_in_function_t<std::string, internal::date_string, Args...> date(Args... args) { + return {std::tuple<Args...>{std::forward<Args>(args)...}}; } /** * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template<class... Args> - internal::core_function_t<std::string, internal::time_string, Args...> time(Args... args) { - std::tuple<Args...> t{std::forward<Args>(args)...}; - return {move(t)}; + internal::built_in_function_t<std::string, internal::time_string, Args...> time(Args... args) { + return {std::tuple<Args...>{std::forward<Args>(args)...}}; } /** * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template<class... Args> - internal::core_function_t<std::string, internal::datetime_string, Args...> datetime(Args... args) { - std::tuple<Args...> t{std::forward<Args>(args)...}; - return {move(t)}; + internal::built_in_function_t<std::string, internal::datetime_string, Args...> datetime(Args... args) { + return {std::tuple<Args...>{std::forward<Args>(args)...}}; } /** * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template<class... Args> - internal::core_function_t<double, internal::julianday_string, Args...> julianday(Args... args) { - std::tuple<Args...> t{std::forward<Args>(args)...}; - return {move(t)}; + internal::built_in_function_t<double, internal::julianday_string, Args...> julianday(Args... args) { + return {std::tuple<Args...>{std::forward<Args>(args)...}}; } /** * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template<class... Args> - internal::core_function_t<std::string, internal::strftime_string, Args...> strftime(Args... args) { - std::tuple<Args...> t{std::forward<Args>(args)...}; - return {move(t)}; + internal::built_in_function_t<std::string, internal::strftime_string, Args...> strftime(Args... args) { + return {std::tuple<Args...>{std::forward<Args>(args)...}}; } /** * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob */ template<class N> - internal::core_function_t<std::vector<char>, internal::zeroblob_string, N> zeroblob(N n) { - std::tuple<N> args{std::forward<N>(n)}; - return {move(args)}; + internal::built_in_function_t<std::vector<char>, internal::zeroblob_string, N> zeroblob(N n) { + return {std::tuple<N>{std::forward<N>(n)}}; } /** * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr */ template<class X, class Y> - internal::core_function_t<std::string, internal::substr_string, X, Y> substr(X x, Y y) { - std::tuple<X, Y> args{std::forward<X>(x), std::forward<Y>(y)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::substr_string, X, Y> substr(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; } /** * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr */ template<class X, class Y, class Z> - internal::core_function_t<std::string, internal::substr_string, X, Y, Z> substr(X x, Y y, Z z) { - std::tuple<X, Y, Z> args{std::forward<X>(x), std::forward<Y>(y), std::forward<Z>(z)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::substr_string, X, Y, Z> substr(X x, Y y, Z z) { + return {std::tuple<X, Y, Z>{std::forward<X>(x), std::forward<Y>(y), std::forward<Z>(z)}}; } #ifdef SQLITE_SOUNDEX @@ -4038,8 +5862,7 @@ namespace sqlite_orm { */ template<class X> internal::core_function_t<std::string, internal::soundex_string, X> soundex(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + return {std::tuple<X>{std::forward<X>(x)}}; } #endif @@ -4047,27 +5870,24 @@ namespace sqlite_orm { * TOTAL(X) aggregate function. */ template<class X> - internal::core_function_t<double, internal::total_string, X> total(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<double, internal::total_string, X> total(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * SUM(X) aggregate function. */ template<class X> - internal::core_function_t<std::unique_ptr<double>, internal::sum_string, X> sum(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<std::unique_ptr<double>, internal::sum_string, X> sum(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * COUNT(X) aggregate function. */ template<class X> - internal::core_function_t<int, internal::count_string, X> count(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<int, internal::count_string, X> count(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** @@ -4090,47 +5910,164 @@ namespace sqlite_orm { * AVG(X) aggregate function. */ template<class X> - internal::core_function_t<double, internal::avg_string, X> avg(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<double, internal::avg_string, X> avg(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * MAX(X) aggregate function. */ template<class X> - internal::core_function_t<internal::unique_ptr_result_of<X>, internal::max_string, X> max(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<internal::unique_ptr_result_of<X>, internal::max_string, X> max(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * MIN(X) aggregate function. */ template<class X> - internal::core_function_t<internal::unique_ptr_result_of<X>, internal::min_string, X> min(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<internal::unique_ptr_result_of<X>, internal::min_string, X> min(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * GROUP_CONCAT(X) aggregate function. */ template<class X> - internal::core_function_t<std::string, internal::group_concat_string, X> group_concat(X x) { - std::tuple<X> args{std::forward<X>(x)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::group_concat_string, X> group_concat(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } /** * GROUP_CONCAT(X, Y) aggregate function. */ template<class X, class Y> - internal::core_function_t<std::string, internal::group_concat_string, X, Y> group_concat(X x, Y y) { - std::tuple<X, Y> args{std::forward<X>(x), std::forward<Y>(y)}; - return {move(args)}; + internal::built_in_function_t<std::string, internal::group_concat_string, X, Y> group_concat(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } +#ifdef SQLITE_ENABLE_JSON1 + template<class X> + internal::built_in_function_t<std::string, internal::json_string, X> json(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + template<class... Args> + internal::built_in_function_t<std::string, internal::json_array_string, Args...> json_array(Args... args) { + return {std::tuple<Args...>{std::forward<Args>(args)...}}; + } + + template<class X> + internal::built_in_function_t<int, internal::json_array_length_string, X> json_array_length(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + template<class R, class X> + internal::built_in_function_t<R, internal::json_array_length_string, X> json_array_length(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + template<class X, class Y> + internal::built_in_function_t<int, internal::json_array_length_string, X, Y> json_array_length(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + template<class R, class X, class Y> + internal::built_in_function_t<R, internal::json_array_length_string, X, Y> json_array_length(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + template<class R, class X, class... Args> + internal::built_in_function_t<R, internal::json_extract_string, X, Args...> json_extract(X x, Args... args) { + return {std::tuple<X, Args...>{std::forward<X>(x), std::forward<Args>(args)...}}; + } + + template<class X, class... Args> + internal::built_in_function_t<std::string, internal::json_insert_string, X, Args...> json_insert(X x, + Args... args) { + static_assert(std::tuple_size<std::tuple<Args...>>::value % 2 == 0, + "number of arguments in json_insert must be odd"); + return {std::tuple<X, Args...>{std::forward<X>(x), std::forward<Args>(args)...}}; + } + + template<class X, class... Args> + internal::built_in_function_t<std::string, internal::json_replace_string, X, Args...> json_replace(X x, + Args... args) { + static_assert(std::tuple_size<std::tuple<Args...>>::value % 2 == 0, + "number of arguments in json_replace must be odd"); + return {std::tuple<X, Args...>{std::forward<X>(x), std::forward<Args>(args)...}}; + } + + template<class X, class... Args> + internal::built_in_function_t<std::string, internal::json_set_string, X, Args...> json_set(X x, Args... args) { + static_assert(std::tuple_size<std::tuple<Args...>>::value % 2 == 0, + "number of arguments in json_set must be odd"); + return {std::tuple<X, Args...>{std::forward<X>(x), std::forward<Args>(args)...}}; + } + + template<class... Args> + internal::built_in_function_t<std::string, internal::json_object_string, Args...> json_object(Args... args) { + static_assert(std::tuple_size<std::tuple<Args...>>::value % 2 == 0, + "number of arguments in json_object must be even"); + return {std::tuple<Args...>{std::forward<Args>(args)...}}; + } + + template<class X, class Y> + internal::built_in_function_t<std::string, internal::json_patch_string, X, Y> json_patch(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + template<class X, class... Args> + internal::built_in_function_t<std::string, internal::json_remove_string, X, Args...> json_remove(X x, + Args... args) { + return {std::tuple<X, Args...>{std::forward<X>(x), std::forward<Args>(args)...}}; + } + + template<class R, class X, class... Args> + internal::built_in_function_t<R, internal::json_remove_string, X, Args...> json_remove(X x, Args... args) { + return {std::tuple<X, Args...>{std::forward<X>(x), std::forward<Args>(args)...}}; + } + + template<class X> + internal::built_in_function_t<std::string, internal::json_type_string, X> json_type(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + template<class R, class X> + internal::built_in_function_t<R, internal::json_type_string, X> json_type(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + template<class X, class Y> + internal::built_in_function_t<std::string, internal::json_type_string, X, Y> json_type(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + template<class R, class X, class Y> + internal::built_in_function_t<R, internal::json_type_string, X, Y> json_type(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + + template<class X> + internal::built_in_function_t<bool, internal::json_valid_string, X> json_valid(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + template<class R, class X> + internal::built_in_function_t<R, internal::json_quote_string, X> json_quote(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; + } + + template<class X> + internal::built_in_function_t<std::string, internal::json_group_array_string, X> json_group_array(X x) { + return {std::tuple<X>{std::forward<X>(x)}}; } + template<class X, class Y> + internal::built_in_function_t<std::string, internal::json_group_object_string, X, Y> json_group_object(X x, Y y) { + return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; + } + +#endif // SQLITE_ENABLE_JSON1 template<class L, class R, typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + @@ -4187,20 +6124,20 @@ namespace sqlite_orm { */ template<class L, class R> struct typed_comparator { - bool operator()(const L &, const R &) const { + bool operator()(const L&, const R&) const { return false; } }; template<class O> struct typed_comparator<O, O> { - bool operator()(const O &lhs, const O &rhs) const { + bool operator()(const O& lhs, const O& rhs) const { return lhs == rhs; } }; template<class L, class R> - bool compare_any(const L &lhs, const R &rhs) { + bool compare_any(const L& lhs, const R& rhs) { return typed_comparator<L, R>()(lhs, rhs); } } @@ -4210,41 +6147,127 @@ namespace sqlite_orm { #include <string> // std::string #include <utility> // std::declval #include <tuple> // std::tuple, std::get, std::tuple_size +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include <optional> // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED // #include "is_base_of_template.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "optional_container.h" +// #include "ast/where.h" + +// #include "../serialize_result_type.h" + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include <string_view> // string_view +#else +#include <string> // std::string +#endif + namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; +#else + using serialize_result_type = std::string; +#endif + } +} +namespace sqlite_orm { namespace internal { + struct where_string { + serialize_result_type serialize() const { + return "WHERE"; + } + }; + /** - * DISCTINCT generic container. + * WHERE argument holder. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * Don't construct it manually. Call `where(...)` function instead. */ + template<class C> + struct where_t : where_string { + using expression_type = C; + + expression_type expression; + + where_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + template<class T> - struct distinct_t { - T t; + struct is_where : std::false_type {}; + + template<class T> + struct is_where<where_t<T>> : std::true_type {}; + } + + /** + * WHERE clause. Use it to add WHERE conditions wherever you like. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * @example + * // SELECT name + * // FROM letters + * // WHERE id > 3 + * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); + */ + template<class C> + internal::where_t<C> where(C expression) { + return {std::move(expression)}; + } +} + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<class T> + struct as_optional_t { + using value_type = T; + + value_type value; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + struct distinct_string { operator std::string() const { return "DISTINCT"; } }; /** - * ALL generic container. + * DISCTINCT generic container. */ template<class T> - struct all_t { - T t; + struct distinct_t : distinct_string { + using value_type = T; + value_type value; + + distinct_t(value_type value_) : value(std::move(value_)) {} + }; + + struct all_string { operator std::string() const { return "ALL"; } }; + /** + * ALL generic container. + */ + template<class T> + struct all_t : all_string { + T value; + + all_t(T value_) : value(std::move(value_)) {} + }; + template<class... Args> struct columns_t { using columns_type = std::tuple<Args...>; @@ -4255,6 +6278,12 @@ namespace sqlite_orm { static constexpr const int count = std::tuple_size<columns_type>::value; }; + template<class T> + struct is_columns : std::false_type {}; + + template<class... Args> + struct is_columns<columns_t<Args...>> : std::true_type {}; + struct set_string { operator std::string() const { return "SET"; @@ -4276,10 +6305,41 @@ namespace sqlite_orm { */ template<class T, class F> struct column_pointer { + using self = column_pointer<T, F>; using type = T; using field_type = F; field_type field; + + template<class R> + internal::is_equal_t<self, R> operator==(R rhs) const { + return {*this, std::move(rhs)}; + } + + template<class R> + internal::is_not_equal_t<self, R> operator!=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template<class R> + internal::lesser_than_t<self, R> operator<(R rhs) const { + return {*this, std::move(rhs)}; + } + + template<class R> + internal::lesser_or_equal_t<self, R> operator<=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template<class R> + internal::greater_than_t<self, R> operator>(R rhs) const { + return {*this, std::move(rhs)}; + } + + template<class R> + internal::greater_or_equal_t<self, R> operator>=(R rhs) const { + return {*this, std::move(rhs)}; + } }; /** @@ -4295,6 +6355,12 @@ namespace sqlite_orm { bool highest_level = false; }; + template<class T> + struct is_select : std::false_type {}; + + template<class T, class... Args> + struct is_select<select_t<T, Args...>> : std::true_type {}; + /** * Base for UNION, UNION ALL, EXCEPT and INTERSECT */ @@ -4338,48 +6404,51 @@ namespace sqlite_orm { union_t(left_type l, right_type r) : union_t(std::move(l), std::move(r), false) {} }; + struct except_string { + operator std::string() const { + return "EXCEPT"; + } + }; + /** * EXCEPT object type. */ template<class L, class R> - struct except_t : public compound_operator<L, R> { + struct except_t : compound_operator<L, R>, except_string { using super = compound_operator<L, R>; using left_type = typename super::left_type; using right_type = typename super::right_type; using super::super; + }; + struct intersect_string { operator std::string() const { - return "EXCEPT"; + return "INTERSECT"; } }; - /** * INTERSECT object type. */ template<class L, class R> - struct intersect_t : public compound_operator<L, R> { + struct intersect_t : compound_operator<L, R>, intersect_string { using super = compound_operator<L, R>; using left_type = typename super::left_type; using right_type = typename super::right_type; using super::super; - - operator std::string() const { - return "INTERSECT"; - } }; /** * Generic way to get DISTINCT value from any type. */ template<class T> - bool get_distinct(const T &) { + bool get_distinct(const T&) { return false; } template<class... Args> - bool get_distinct(const columns_t<Args...> &cols) { + bool get_distinct(const columns_t<Args...>& cols) { return cols.distinct; } @@ -4454,9 +6523,16 @@ namespace sqlite_orm { static_assert(count_tuple<T, is_group_by>::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); static_assert(count_tuple<T, is_order_by>::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); static_assert(count_tuple<T, is_limit>::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); + static_assert(count_tuple<T, is_from>::value <= 1, "a single query cannot contain > 1 FROM blocks"); } } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<class T> + internal::as_optional_t<T> as_optional(T value) { + return {std::move(value)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> internal::then_t<T> then(T t) { return {std::move(t)}; @@ -4587,48 +6663,6 @@ namespace sqlite_orm { } #pragma once -#include <type_traits> // std::enable_if, std::is_member_pointer - -// #include "select_constraints.h" - -// #include "column.h" - -namespace sqlite_orm { - - namespace internal { - - /** - * Trait class used to define table mapped type by setter/getter/member - * T - member pointer - */ - template<class T, class SFINAE = void> - struct table_type; - - template<class O, class F> - struct table_type<F O::*, - typename std::enable_if<std::is_member_pointer<F O::*>::value && - !std::is_member_function_pointer<F O::*>::value>::type> { - using type = O; - }; - - template<class T> - struct table_type<T, typename std::enable_if<is_getter<T>::value>::type> { - using type = typename getter_traits<T>::object_type; - }; - - template<class T> - struct table_type<T, typename std::enable_if<is_setter<T>::value>::type> { - using type = typename setter_traits<T>::object_type; - }; - - template<class T, class F> - struct table_type<column_pointer<T, F>, void> { - using type = T; - }; - } -} -#pragma once - #include <string> // std::string namespace sqlite_orm { @@ -4640,27 +6674,32 @@ namespace sqlite_orm { bool notnull = false; std::string dflt_value; int pk = 0; + + table_info(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_) : + cid(cid_), + name(move(name_)), type(move(type_)), notnull(notnull_), dflt_value(move(dflt_value_)), pk(pk_) {} }; } #pragma once +#include <memory> // std::unique_ptr #include <sqlite3.h> +#include <type_traits> // std::integral_constant namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor */ - struct statement_finalizer { - sqlite3_stmt *stmt = nullptr; + using statement_finalizer = + std::unique_ptr<sqlite3_stmt, std::integral_constant<decltype(&sqlite3_finalize), sqlite3_finalize>>; - statement_finalizer(decltype(stmt) stmt_) : stmt(stmt_) {} - - inline ~statement_finalizer() { - sqlite3_finalize(this->stmt); - } - }; } #pragma once @@ -4697,6 +6736,7 @@ namespace sqlite_orm { #include <cstddef> // std::nullptr_t #include <utility> // std::declval #include <locale> // std::wstring_convert +#include <cstring> // ::strncpy, ::strlen // #include "is_std_ptr.h" @@ -4712,7 +6752,7 @@ namespace sqlite_orm { struct is_std_ptr<std::shared_ptr<T>> : std::true_type { using element_type = T; - static std::shared_ptr<T> make(const T &v) { + static std::shared_ptr<T> make(const T& v) { return std::make_shared<T>(v); } }; @@ -4721,7 +6761,7 @@ namespace sqlite_orm { struct is_std_ptr<std::unique_ptr<T>> : std::true_type { using element_type = T; - static std::unique_ptr<T> make(const T &v) { + static std::unique_ptr<T> make(const T& v) { return std::make_unique<T>(v); } }; @@ -4740,24 +6780,41 @@ namespace sqlite_orm { */ template<class V> struct statement_binder<V, std::enable_if_t<std::is_arithmetic<V>::value>> { - int bind(sqlite3_stmt *stmt, int index, const V &value) { - return bind(stmt, index, value, tag()); + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + return this->bind(stmt, index, value, tag()); + } + + void result(sqlite3_context* context, const V& value) const { + this->result(context, value, tag()); } private: using tag = arithmetic_tag_t<V>; - int bind(sqlite3_stmt *stmt, int index, const V &value, const int_or_smaller_tag &) { + int bind(sqlite3_stmt* stmt, int index, const V& value, const int_or_smaller_tag&) const { return sqlite3_bind_int(stmt, index, static_cast<int>(value)); } - int bind(sqlite3_stmt *stmt, int index, const V &value, const bigint_tag &) { + void result(sqlite3_context* context, const V& value, const int_or_smaller_tag&) const { + sqlite3_result_int(context, static_cast<int>(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, const bigint_tag&) const { return sqlite3_bind_int64(stmt, index, static_cast<sqlite3_int64>(value)); } - int bind(sqlite3_stmt *stmt, int index, const V &value, const real_tag &) { + void result(sqlite3_context* context, const V& value, const bigint_tag&) const { + sqlite3_result_int64(context, static_cast<sqlite3_int64>(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, const real_tag&) const { return sqlite3_bind_double(stmt, index, static_cast<double>(value)); } + + void result(sqlite3_context* context, const V& value, const real_tag&) const { + sqlite3_result_double(context, static_cast<double>(value)); + } }; /** @@ -4766,18 +6823,33 @@ namespace sqlite_orm { template<class V> struct statement_binder< V, - std::enable_if_t<std::is_same<V, std::string>::value || std::is_same<V, const char *>::value>> { - int bind(sqlite3_stmt *stmt, int index, const V &value) { - return sqlite3_bind_text(stmt, index, string_data(value), -1, SQLITE_TRANSIENT); + std::enable_if_t<std::is_same<V, std::string>::value || std::is_same<V, const char*>::value>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + return sqlite3_bind_text(stmt, index, std::get<0>(stringData), std::get<1>(stringData), SQLITE_TRANSIENT); + } + + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + auto stringDataLength = std::get<1>(stringData); + auto dataCopy = new char[stringDataLength + 1]; + auto stringChars = std::get<0>(stringData); + ::strncpy(dataCopy, stringChars, stringDataLength + 1); + sqlite3_result_text(context, dataCopy, stringDataLength, [](void* pointer) { + auto charPointer = (char*)pointer; + delete[] charPointer; + }); } private: - const char *string_data(const std::string &s) const { - return s.c_str(); + std::tuple<const char*, int> string_data(const std::string& s) const { + return {s.c_str(), int(s.size())}; } - const char *string_data(const char *s) const { - return s; + std::tuple<const char*, int> string_data(const char* s) const { + auto length = int(::strlen(s)); + return {s, length}; } }; @@ -4788,12 +6860,17 @@ namespace sqlite_orm { template<class V> struct statement_binder< V, - std::enable_if_t<std::is_same<V, std::wstring>::value || std::is_same<V, const wchar_t *>::value>> { - int bind(sqlite3_stmt *stmt, int index, const V &value) { + std::enable_if_t<std::is_same<V, std::wstring>::value || std::is_same<V, const wchar_t*>::value>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; std::string utf8Str = converter.to_bytes(value); return statement_binder<decltype(utf8Str)>().bind(stmt, index, utf8Str); } + + void result(sqlite3_context* context, const V& value) const { + sqlite3_result_text16(context, (const void*)value.data(), int(value.length()), nullptr); + } }; #endif // SQLITE_ORM_OMITS_CODECVT @@ -4802,17 +6879,28 @@ namespace sqlite_orm { */ template<> struct statement_binder<std::nullptr_t, void> { - int bind(sqlite3_stmt *stmt, int index, const std::nullptr_t &) { + int bind(sqlite3_stmt* stmt, int index, const std::nullptr_t&) const { return sqlite3_bind_null(stmt, index); } + + void result(sqlite3_context* context, const std::nullptr_t&) const { + sqlite3_result_null(context); + } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Specialization for std::nullopt_t. + */ template<> struct statement_binder<std::nullopt_t, void> { - int bind(sqlite3_stmt *stmt, int index, const std::nullopt_t &) { + int bind(sqlite3_stmt* stmt, int index, const std::nullopt_t&) const { return sqlite3_bind_null(stmt, index); } + + void result(sqlite3_context* context, const std::nullopt_t&) const { + sqlite3_result_null(context); + } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -4820,13 +6908,21 @@ namespace sqlite_orm { struct statement_binder<V, std::enable_if_t<is_std_ptr<V>::value>> { using value_type = typename is_std_ptr<V>::element_type; - int bind(sqlite3_stmt *stmt, int index, const V &value) { + int bind(sqlite3_stmt* stmt, int index, const V& value) const { if(value) { return statement_binder<value_type>().bind(stmt, index, *value); } else { return statement_binder<std::nullptr_t>().bind(stmt, index, nullptr); } } + + void result(sqlite3_context* context, const V& value) const { + if(value) { + statement_binder<value_type>().result(context, value); + } else { + statement_binder<std::nullptr_t>().result(context, nullptr); + } + } }; /** @@ -4834,17 +6930,21 @@ namespace sqlite_orm { */ template<> struct statement_binder<std::vector<char>, void> { - int bind(sqlite3_stmt *stmt, int index, const std::vector<char> &value) { + int bind(sqlite3_stmt* stmt, int index, const std::vector<char>& value) const { if(value.size()) { - return sqlite3_bind_blob(stmt, - index, - (const void *)&value.front(), - int(value.size()), - SQLITE_TRANSIENT); + return sqlite3_bind_blob(stmt, index, (const void*)&value.front(), int(value.size()), SQLITE_TRANSIENT); } else { return sqlite3_bind_blob(stmt, index, "", 0, SQLITE_TRANSIENT); } } + + void result(sqlite3_context* context, const std::vector<char>& value) const { + if(value.size()) { + sqlite3_result_blob(context, (const void*)&value.front(), int(value.size()), nullptr); + } else { + sqlite3_result_blob(context, "", 0, nullptr); + } + } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -4852,13 +6952,21 @@ namespace sqlite_orm { struct statement_binder<std::optional<T>, void> { using value_type = T; - int bind(sqlite3_stmt *stmt, int index, const std::optional<T> &value) { + int bind(sqlite3_stmt* stmt, int index, const std::optional<T>& value) const { if(value) { return statement_binder<value_type>().bind(stmt, index, *value); } else { return statement_binder<std::nullopt_t>().bind(stmt, index, std::nullopt); } } + + void result(sqlite3_context* context, const std::optional<T>& value) const { + if(value) { + statement_binder<value_type>().result(context, value); + } else { + statement_binder<std::nullopt_t>().result(context, std::nullopt); + } + } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -4868,8 +6976,8 @@ namespace sqlite_orm { using is_bindable = std::integral_constant<bool, !std::is_base_of<std::false_type, statement_binder<T>>::value>; struct conditional_binder_base { - sqlite3_stmt *stmt = nullptr; - int &index; + sqlite3_stmt* stmt = nullptr; + int& index; conditional_binder_base(decltype(stmt) stmt_, decltype(index) index_) : stmt(stmt_), index(index_) {} }; @@ -4882,7 +6990,7 @@ namespace sqlite_orm { using conditional_binder_base::conditional_binder_base; - int operator()(const T &t) const { + int operator()(const T& t) const { return statement_binder<T>().bind(this->stmt, this->index++, t); } }; @@ -4891,24 +6999,35 @@ namespace sqlite_orm { struct conditional_binder<T, std::false_type> : conditional_binder_base { using conditional_binder_base::conditional_binder_base; - int operator()(const T &) const { + int operator()(const T&) const { return SQLITE_OK; } }; - template<class T, class SFINAE = void> - struct bindable_filter_single; + template<class T, template<class C> class F, class SFINAE = void> + struct tuple_filter_single; - template<class T> - struct bindable_filter_single<T, typename std::enable_if<is_bindable<T>::value>::type> { + template<class T, template<class C> class F> + struct tuple_filter_single<T, F, typename std::enable_if<F<T>::value>::type> { using type = std::tuple<T>; }; - template<class T> - struct bindable_filter_single<T, typename std::enable_if<!is_bindable<T>::value>::type> { + template<class T, template<class C> class F> + struct tuple_filter_single<T, F, typename std::enable_if<!F<T>::value>::type> { using type = std::tuple<>; }; + template<class T, template<class C> class F> + struct tuple_filter; + + template<class... Args, template<class C> class F> + struct tuple_filter<std::tuple<Args...>, F> { + using type = typename conc_tuple<typename tuple_filter_single<Args, F>::type...>::type; + }; + + template<class T> + struct bindable_filter_single : tuple_filter_single<T, is_bindable> {}; + template<class T> struct bindable_filter; @@ -4922,7 +7041,7 @@ namespace sqlite_orm { #include <sqlite3.h> #include <type_traits> // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include <cstdlib> // atof, atoi, atoll +#include <stdlib.h> // atof, atoi, atoll #include <string> // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT #include <codecvt> // std::wstring_convert, std::codecvt_utf8_utf16 @@ -4962,7 +7081,7 @@ namespace sqlite_orm { namespace internal { - inline const std::string &to_string(journal_mode j) { + inline const std::string& to_string(journal_mode j) { static std::string res[] = { "DELETE", "TRUNCATE", @@ -4974,7 +7093,7 @@ namespace sqlite_orm { return res[static_cast<int>(j)]; } - inline std::unique_ptr<journal_mode> journal_mode_from_string(const std::string &str) { + inline std::unique_ptr<journal_mode> journal_mode_from_string(const std::string& str) { std::string upper_str; std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { return static_cast<char>(std::toupper(static_cast<int>(c))); @@ -5009,10 +7128,13 @@ namespace sqlite_orm { template<class V, typename Enable = void> struct row_extractor { // used in sqlite3_exec (select) - V extract(const char *row_value); + V extract(const char* row_value) const; // used in sqlite_column (iteration, get_all) - V extract(sqlite3_stmt *stmt, int columnIndex); + V extract(sqlite3_stmt* stmt, int columnIndex) const; + + // used in user defined functions + V extract(sqlite3_value* value) const; }; /** @@ -5020,40 +7142,56 @@ namespace sqlite_orm { */ template<class V> struct row_extractor<V, std::enable_if_t<std::is_arithmetic<V>::value>> { - V extract(const char *row_value) { - return extract(row_value, tag()); + V extract(const char* row_value) const { + return this->extract(row_value, tag()); } - V extract(sqlite3_stmt *stmt, int columnIndex) { - return extract(stmt, columnIndex, tag()); + V extract(sqlite3_stmt* stmt, int columnIndex) const { + return this->extract(stmt, columnIndex, tag()); + } + + V extract(sqlite3_value* value) const { + return this->extract(value, tag()); } private: using tag = arithmetic_tag_t<V>; - V extract(const char *row_value, const int_or_smaller_tag &) { + V extract(const char* row_value, const int_or_smaller_tag&) const { return static_cast<V>(atoi(row_value)); } - V extract(sqlite3_stmt *stmt, int columnIndex, const int_or_smaller_tag &) { + V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { return static_cast<V>(sqlite3_column_int(stmt, columnIndex)); } - V extract(const char *row_value, const bigint_tag &) { + V extract(sqlite3_value* value, const int_or_smaller_tag&) const { + return static_cast<V>(sqlite3_value_int(value)); + } + + V extract(const char* row_value, const bigint_tag&) const { return static_cast<V>(atoll(row_value)); } - V extract(sqlite3_stmt *stmt, int columnIndex, const bigint_tag &) { + V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { return static_cast<V>(sqlite3_column_int64(stmt, columnIndex)); } - V extract(const char *row_value, const real_tag &) { + V extract(sqlite3_value* value, const bigint_tag&) const { + return static_cast<V>(sqlite3_value_int64(value)); + } + + V extract(const char* row_value, const real_tag&) const { return static_cast<V>(atof(row_value)); } - V extract(sqlite3_stmt *stmt, int columnIndex, const real_tag &) { + V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { return static_cast<V>(sqlite3_column_double(stmt, columnIndex)); } + + V extract(sqlite3_value* value, const real_tag&) const { + return static_cast<V>(sqlite3_value_double(value)); + } }; /** @@ -5061,7 +7199,7 @@ namespace sqlite_orm { */ template<> struct row_extractor<std::string, void> { - std::string extract(const char *row_value) { + std::string extract(const char* row_value) const { if(row_value) { return row_value; } else { @@ -5069,9 +7207,16 @@ namespace sqlite_orm { } } - std::string extract(sqlite3_stmt *stmt, int columnIndex) { - auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); - if(cStr) { + std::string extract(sqlite3_stmt* stmt, int columnIndex) const { + if(auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex)) { + return cStr; + } else { + return {}; + } + } + + std::string extract(sqlite3_value* value) const { + if(auto cStr = (const char*)sqlite3_value_text(value)) { return cStr; } else { return {}; @@ -5084,7 +7229,7 @@ namespace sqlite_orm { */ template<> struct row_extractor<std::wstring, void> { - std::wstring extract(const char *row_value) { + std::wstring extract(const char* row_value) const { if(row_value) { std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; return converter.from_bytes(row_value); @@ -5093,8 +7238,8 @@ namespace sqlite_orm { } } - std::wstring extract(sqlite3_stmt *stmt, int columnIndex) { - auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); + std::wstring extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); if(cStr) { std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; return converter.from_bytes(cStr); @@ -5102,6 +7247,14 @@ namespace sqlite_orm { return {}; } } + + std::wstring extract(sqlite3_value* value) const { + if(auto cStr = (const wchar_t*)sqlite3_value_text16(value)) { + return cStr; + } else { + return {}; + } + } }; #endif // SQLITE_ORM_OMITS_CODECVT @@ -5109,7 +7262,7 @@ namespace sqlite_orm { struct row_extractor<V, std::enable_if_t<is_std_ptr<V>::value>> { using value_type = typename is_std_ptr<V>::element_type; - V extract(const char *row_value) { + V extract(const char* row_value) const { if(row_value) { return is_std_ptr<V>::make(row_extractor<value_type>().extract(row_value)); } else { @@ -5117,7 +7270,7 @@ namespace sqlite_orm { } } - V extract(sqlite3_stmt *stmt, int columnIndex) { + V extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { return is_std_ptr<V>::make(row_extractor<value_type>().extract(stmt, columnIndex)); @@ -5125,6 +7278,15 @@ namespace sqlite_orm { return {}; } } + + V extract(sqlite3_value* value) const { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + return is_std_ptr<V>::make(row_extractor<value_type>().extract(value)); + } else { + return {}; + } + } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -5132,7 +7294,7 @@ namespace sqlite_orm { struct row_extractor<std::optional<T>, void> { using value_type = T; - std::optional<T> extract(const char *row_value) { + std::optional<T> extract(const char* row_value) const { if(row_value) { return std::make_optional(row_extractor<value_type>().extract(row_value)); } else { @@ -5140,7 +7302,7 @@ namespace sqlite_orm { } } - std::optional<T> extract(sqlite3_stmt *stmt, int columnIndex) { + std::optional<T> extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { return std::make_optional(row_extractor<value_type>().extract(stmt, columnIndex)); @@ -5148,6 +7310,15 @@ namespace sqlite_orm { return std::nullopt; } } + + std::optional<T> extract(sqlite3_value* value) const { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + return std::make_optional(row_extractor<value_type>().extract(value)); + } else { + return std::nullopt; + } + } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED /** @@ -5155,7 +7326,7 @@ namespace sqlite_orm { */ template<> struct row_extractor<std::vector<char>> { - std::vector<char> extract(const char *row_value) { + std::vector<char> extract(const char* row_value) const { if(row_value) { auto len = ::strlen(row_value); return this->go(row_value, len); @@ -5164,14 +7335,20 @@ namespace sqlite_orm { } } - std::vector<char> extract(sqlite3_stmt *stmt, int columnIndex) { - auto bytes = static_cast<const char *>(sqlite3_column_blob(stmt, columnIndex)); + std::vector<char> extract(sqlite3_stmt* stmt, int columnIndex) const { + auto bytes = static_cast<const char*>(sqlite3_column_blob(stmt, columnIndex)); auto len = static_cast<size_t>(sqlite3_column_bytes(stmt, columnIndex)); return this->go(bytes, len); } + std::vector<char> extract(sqlite3_value* value) const { + auto bytes = static_cast<const char*>(sqlite3_value_blob(value)); + auto len = static_cast<size_t>(sqlite3_value_bytes(value)); + return this->go(bytes, len); + } + protected: - std::vector<char> go(const char *bytes, size_t len) { + std::vector<char> go(const char* bytes, size_t len) const { if(len) { std::vector<char> res; res.reserve(len); @@ -5186,40 +7363,40 @@ namespace sqlite_orm { template<class... Args> struct row_extractor<std::tuple<Args...>> { - std::tuple<Args...> extract(char **argv) { + std::tuple<Args...> extract(char** argv) const { std::tuple<Args...> res; this->extract<std::tuple_size<decltype(res)>::value>(res, argv); return res; } - std::tuple<Args...> extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + std::tuple<Args...> extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { std::tuple<Args...> res; this->extract<std::tuple_size<decltype(res)>::value>(res, stmt); return res; } protected: - template<size_t I, typename std::enable_if<I != 0>::type * = nullptr> - void extract(std::tuple<Args...> &t, sqlite3_stmt *stmt) { + template<size_t I, typename std::enable_if<I != 0>::type* = nullptr> + void extract(std::tuple<Args...>& t, sqlite3_stmt* stmt) const { using tuple_type = typename std::tuple_element<I - 1, typename std::tuple<Args...>>::type; std::get<I - 1>(t) = row_extractor<tuple_type>().extract(stmt, I - 1); this->extract<I - 1>(t, stmt); } - template<size_t I, typename std::enable_if<I == 0>::type * = nullptr> - void extract(std::tuple<Args...> &, sqlite3_stmt *) { + template<size_t I, typename std::enable_if<I == 0>::type* = nullptr> + void extract(std::tuple<Args...>&, sqlite3_stmt*) const { //.. } - template<size_t I, typename std::enable_if<I != 0>::type * = nullptr> - void extract(std::tuple<Args...> &t, char **argv) { + template<size_t I, typename std::enable_if<I != 0>::type* = nullptr> + void extract(std::tuple<Args...>& t, char** argv) const { using tuple_type = typename std::tuple_element<I - 1, typename std::tuple<Args...>>::type; std::get<I - 1>(t) = row_extractor<tuple_type>().extract(argv[I - 1]); this->extract<I - 1>(t, argv); } - template<size_t I, typename std::enable_if<I == 0>::type * = nullptr> - void extract(std::tuple<Args...> &, char **) { + template<size_t I, typename std::enable_if<I == 0>::type* = nullptr> + void extract(std::tuple<Args...>&, char**) const { //.. } }; @@ -5229,7 +7406,7 @@ namespace sqlite_orm { */ template<> struct row_extractor<journal_mode, void> { - journal_mode extract(const char *row_value) { + journal_mode extract(const char* row_value) const { if(row_value) { if(auto res = internal::journal_mode_from_string(row_value)) { return std::move(*res); @@ -5241,14 +7418,58 @@ namespace sqlite_orm { } } - journal_mode extract(sqlite3_stmt *stmt, int columnIndex) { - auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); + journal_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); return this->extract(cStr); } }; } #pragma once +#include <sqlite3.h> +#include <string> // std::string +#include <system_error> // std::system_error, std::error_code + +namespace sqlite_orm { + + namespace internal { + inline void perform_step(sqlite3* db, sqlite3_stmt* stmt) { + auto rc = sqlite3_step(stmt); + if(rc == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + static void perform_void_exec(sqlite3* db, const std::string& query) { + int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template<class T> + inline auto call_insert_impl_and_catch_constraint_failed(const T& insert_impl) { + try { + return insert_impl(); + } catch(const std::system_error& e) { + if(e.code() == std::error_code(SQLITE_CONSTRAINT, get_sqlite_error_category())) { + std::stringstream ss; + ss << "Attempting to execute 'insert' request resulted in an error like \"" << e.what() + << "\". Perhaps ordinary 'insert' is not acceptable for this table and you should try " + "'replace' or 'insert' with explicit column listing?"; + throw std::system_error(e.code(), ss.str()); + } + throw; + } + } + } +} +#pragma once + #include <ostream> namespace sqlite_orm { @@ -5290,7 +7511,7 @@ namespace sqlite_orm { dropped_and_recreated, }; - inline std::ostream &operator<<(std::ostream &os, sync_schema_result value) { + inline std::ostream& operator<<(std::ostream& os, sync_schema_result value) { switch(value) { case sync_schema_result::new_table_created: return os << "new table created"; @@ -5326,6 +7547,9 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; + indexed_column_t(column_type _column_or_expression) : + column_or_expression(std::move(_column_or_expression)) {} + column_type column_or_expression; std::string _collation_name; int _order = 0; // -1 = desc, 1 = asc, 0 = not specified @@ -5395,26 +7619,26 @@ namespace sqlite_orm { bool unique = false; }; - template<class... Cols> + template<class... Els> struct index_t : index_base { - using columns_type = std::tuple<Cols...>; + using elements_type = std::tuple<Els...>; using object_type = void; - index_t(std::string name_, bool unique_, columns_type columns_) : - index_base{move(name_), unique_}, columns(move(columns_)) {} + index_t(std::string name_, bool unique_, elements_type elements_) : + index_base{move(name_), unique_}, elements(move(elements_)) {} - columns_type columns; + elements_type elements; }; } template<class... Cols> - internal::index_t<typename internal::indexed_column_maker<Cols>::type...> make_index(const std::string &name, + internal::index_t<typename internal::indexed_column_maker<Cols>::type...> make_index(const std::string& name, Cols... cols) { return {name, false, std::make_tuple(internal::make_indexed_column(cols)...)}; } template<class... Cols> - internal::index_t<typename internal::indexed_column_maker<Cols>::type...> make_unique_index(const std::string &name, + internal::index_t<typename internal::indexed_column_maker<Cols>::type...> make_unique_index(const std::string& name, Cols... cols) { return {name, true, std::make_tuple(internal::make_indexed_column(cols)...)}; } @@ -5540,9 +7764,12 @@ namespace sqlite_orm { template<class... Ts> struct storage_impl; - template<class T, class... Args> + template<class T, bool WithoutRowId, class... Cs> struct table_t; + template<class A, class B> + struct foreign_key_t; + namespace storage_traits { /** @@ -5604,7 +7831,7 @@ namespace sqlite_orm { S, T, typename std::enable_if<std::is_same<T, typename S::table_type::object_type>::value>::type> - : std::integral_constant<int, S::table_type::columns_count> {}; + : std::integral_constant<int, S::table_type::elements_count> {}; template<class S, class T> struct storage_columns_count_impl< @@ -5622,9 +7849,12 @@ namespace sqlite_orm { /** * type is std::tuple of field types of mapped colums. */ - template<class T, class... Args> - struct table_types<table_t<T, Args...>> { - using type = std::tuple<typename Args::field_type...>; + template<class T, bool WithoutRowId, class... Args> + struct table_types<table_t<T, WithoutRowId, Args...>> { + using args_tuple = std::tuple<Args...>; + using columns_tuple = typename tuple_filter<args_tuple, is_column>::type; + + using type = typename tuple_transformer<columns_tuple, column_field_type>::type; }; /** @@ -5665,14 +7895,428 @@ namespace sqlite_orm { typename std::enable_if<!std::is_same<T, typename S::table_type::object_type>::value>::type> : storage_mapped_columns_impl<typename S::super, T> {}; + /** + * C is any column type: column_t or constraint type + * O - object type references in FOREIGN KEY + */ + template<class C, class O> + struct column_foreign_keys_count : std::integral_constant<int, 0> {}; + + template<class A, class B, class O> + struct column_foreign_keys_count<foreign_key_t<A, B>, O> { + using target_type = typename foreign_key_t<A, B>::target_type; + + static constexpr const int value = std::is_same<O, target_type>::value ? 1 : 0; + }; + + /** + * O - object type references in FOREIGN KEY + * Cs - column types which are stored in table_t::columns_type + */ + template<class O, class... Cs> + struct table_foreign_keys_count_impl; + + template<class O> + struct table_foreign_keys_count_impl<O> { + static constexpr const int value = 0; + }; + + template<class O, class H, class... Tail> + struct table_foreign_keys_count_impl<O, H, Tail...> { + static constexpr const int value = + column_foreign_keys_count<H, O>::value + table_foreign_keys_count_impl<O, Tail...>::value; + }; + + /** + * T is table_t type + * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) + */ + template<class T, class O> + struct table_foreign_keys_count; + + template<class T, class... Cs, class O> + struct table_foreign_keys_count<table_t<T, false, Cs...>, O> { + using table_type = table_t<T, false, Cs...>; + + static constexpr const int value = table_foreign_keys_count_impl<O, Cs...>::value; + }; + + /** + * S - storage class + * O - type mapped to S + */ + template<class S, class O> + struct storage_foreign_keys_count_impl; + + template<class O> + struct storage_foreign_keys_count_impl<storage_impl<>, O> : std::integral_constant<int, 0> {}; + + template<class H, class... Ts, class O> + struct storage_foreign_keys_count_impl<storage_impl<H, Ts...>, O> { + static constexpr const int value = table_foreign_keys_count<H, O>::value + + storage_foreign_keys_count_impl<storage_impl<Ts...>, O>::value; + }; + + /** + * S - storage class + * O - type mapped to S + * This class tells how many types mapped to S have foreign keys to O + */ + template<class S, class O> + struct storage_foreign_keys_count { + using impl_type = typename S::impl_type; + + static constexpr const int value = storage_foreign_keys_count_impl<impl_type, O>::value; + }; + + /** + * C is any table element type: column_t or constraint type + * O - object type references in FOREIGN KEY + */ + template<class C, class O, class SFINAE = void> + struct column_fk_references { + using type = std::tuple<>; + }; + + template<class C, class O, class SFINAE = void> + struct column_foreign_keys { + using type = std::tuple<>; + }; + + template<class A, class B, class O> + struct column_foreign_keys< + foreign_key_t<A, B>, + O, + typename std::enable_if<std::is_same<O, typename foreign_key_t<A, B>::target_type>::value>::type> { + using type = std::tuple<foreign_key_t<A, B>>; + }; + + template<class A, class B, class O> + struct column_foreign_keys< + foreign_key_t<A, B>, + O, + typename std::enable_if<!std::is_same<O, typename foreign_key_t<A, B>::target_type>::value>::type> { + using type = std::tuple<>; + }; + + template<class A, class B, class O> + struct column_fk_references< + foreign_key_t<A, B>, + O, + typename std::enable_if<std::is_same<O, typename foreign_key_t<A, B>::target_type>::value>::type> { + using target_type = typename foreign_key_t<A, B>::source_type; + + using type = std::tuple<target_type>; + }; + + template<class A, class B, class O> + struct column_fk_references< + foreign_key_t<A, B>, + O, + typename std::enable_if<!std::is_same<O, typename foreign_key_t<A, B>::target_type>::value>::type> { + using type = std::tuple<>; + }; + + /** + * O - object type references in FOREIGN KEY + * Cs - column types which are stored in table_t::columns_type + */ + template<class O, class... Cs> + struct table_fk_references_impl; + + template<class O, class... Cs> + struct table_foreign_keys_impl; + + template<class O> + struct table_fk_references_impl<O> { + using type = std::tuple<>; + }; + + template<class O> + struct table_foreign_keys_impl<O> { + using type = std::tuple<>; + }; + + template<class O, class H, class... Tail> + struct table_fk_references_impl<O, H, Tail...> { + using head_tuple = typename column_fk_references<H, O>::type; + using tail_tuple = typename table_fk_references_impl<O, Tail...>::type; + using type = typename conc_tuple<head_tuple, tail_tuple>::type; + }; + + template<class O, class H, class... Tail> + struct table_foreign_keys_impl<O, H, Tail...> { + using head_tuple = typename column_foreign_keys<H, O>::type; + using tail_tuple = typename table_foreign_keys_impl<O, Tail...>::type; + using type = typename conc_tuple<head_tuple, tail_tuple>::type; + }; + + /** + * T is table_t type + * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) + */ + template<class T, class O> + struct table_fk_references; + + template<class T, class O> + struct table_foreign_keys; + + template<class T, bool WithoutRowId, class... Cs, class O> + struct table_fk_references<table_t<T, WithoutRowId, Cs...>, O> { + using table_type = table_t<T, WithoutRowId, Cs...>; + + using type = typename table_fk_references_impl<O, Cs...>::type; + }; + + template<class T, bool WithoutRowId, class... Cs, class O> + struct table_foreign_keys<table_t<T, WithoutRowId, Cs...>, O> { + using table_type = table_t<T, WithoutRowId, Cs...>; + + using type = typename table_foreign_keys_impl<O, Cs...>::type; + }; + + /** + * S - storage class + * O - type mapped to S + */ + template<class S, class O> + struct storage_fk_references_impl; + + template<class S, class O> + struct storage_foreign_keys_impl; + + template<class O> + struct storage_fk_references_impl<storage_impl<>, O> { + using type = std::tuple<>; + }; + + template<class O> + struct storage_foreign_keys_impl<storage_impl<>, O> { + using type = std::tuple<>; + }; + + template<class H, class... Ts, class O> + struct storage_fk_references_impl<storage_impl<H, Ts...>, O> { + using head_tuple = typename table_fk_references<H, O>::type; + using tail_tuple = typename storage_fk_references_impl<storage_impl<Ts...>, O>::type; + using type = typename conc_tuple<head_tuple, tail_tuple>::type; + }; + + template<class H, class... Ts, class O> + struct storage_foreign_keys_impl<storage_impl<H, Ts...>, O> { + using head_tuple = typename table_foreign_keys<H, O>::type; + using tail_tuple = typename storage_foreign_keys_impl<storage_impl<Ts...>, O>::type; + using type = typename conc_tuple<head_tuple, tail_tuple>::type; + }; + + /** + * S - storage class + * O - type mapped to S + * type holds `std::tuple` with types that has references to O as foreign keys + */ + template<class S, class O> + struct storage_fk_references { + using impl_type = typename S::impl_type; + + using type = typename storage_fk_references_impl<impl_type, O>::type; + }; + + template<class S, class O> + struct storage_foreign_keys { + using impl_type = typename S::impl_type; + + using type = typename storage_foreign_keys_impl<impl_type, O>::type; + }; + } } } +// #include "function.h" + +#include <string> // std::string +#include <sqlite3.h> +#include <tuple> // std::tuple +#include <functional> // std::function + namespace sqlite_orm { - using int64 = sqlite_int64; - using uint64 = sqlite_uint64; + struct arg_values; + + namespace internal { + + struct function_base { + using func_call = std::function< + void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; + using final_call = std::function<void(sqlite3_context* context, void* functionPointer)>; + + std::string name; + int argumentsCount = 0; + std::function<int*()> create; + void (*destroy)(int*) = nullptr; + + function_base(decltype(name) name_, + decltype(argumentsCount) argumentsCount_, + decltype(create) create_, + decltype(destroy) destroy_) : + name(move(name_)), + argumentsCount(argumentsCount_), create(move(create_)), destroy(destroy_) {} + }; + + struct scalar_function_t : function_base { + func_call run; + + scalar_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(run) run_, + decltype(destroy) destroy_) : + function_base{move(name_), argumentsCount_, move(create_), destroy_}, + run(move(run_)) {} + }; + + struct aggregate_function_t : function_base { + func_call step; + final_call finalCall; + + aggregate_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(step) step_, + decltype(finalCall) finalCall_, + decltype(destroy) destroy_) : + function_base{move(name_), argumentsCount_, move(create_), destroy_}, + step(move(step_)), finalCall(move(finalCall_)) {} + }; + + // got it from here https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature + template<class F> + struct is_scalar_function_impl { + + template<class U, class T> + struct SFINAE; + + template<class U, class R> + struct SFINAE<U, R (U::*)() const> {}; + + template<class U> + static char test(SFINAE<U, decltype(&U::operator())>*); + + template<class U> + static int test(...); + + static constexpr bool has = sizeof(test<F>(0)) == sizeof(char); + }; + + template<class F> + struct is_aggregate_function_impl { + + template<class U, class T> + struct SFINAE; + + template<class U, class R> + struct SFINAE<U, R (U::*)() const> {}; + + template<class U> + static char test(SFINAE<U, decltype(&U::step)>*); + + template<class U> + static int test(...); + + template<class U> + static char test2(SFINAE<U, decltype(&U::fin)>*); + + template<class U> + static int test2(...); + + static constexpr bool has = sizeof(test<F>(0)) == sizeof(char); + static constexpr bool has2 = sizeof(test2<F>(0)) == sizeof(char); + static constexpr bool has_both = has && has2; + }; + + template<class F> + struct is_scalar_function : std::integral_constant<bool, is_scalar_function_impl<F>::has> {}; + + template<class F> + struct is_aggregate_function : std::integral_constant<bool, is_aggregate_function_impl<F>::has_both> {}; + + template<class F> + struct scalar_run_member_pointer { + using type = decltype(&F::operator()); + }; + + template<class F> + struct aggregate_run_member_pointer { + using step_type = decltype(&F::step); + using fin_type = decltype(&F::fin); + }; + + template<class T> + struct member_function_arguments; + + template<class O, class R, class... Args> + struct member_function_arguments<R (O::*)(Args...) const> { + using member_function_type = R (O::*)(Args...) const; + using tuple_type = std::tuple<typename std::decay<Args>::type...>; + using return_type = R; + }; + + template<class O, class R, class... Args> + struct member_function_arguments<R (O::*)(Args...)> { + using member_function_type = R (O::*)(Args...); + using tuple_type = std::tuple<typename std::decay<Args>::type...>; + using return_type = R; + }; + + template<class F, bool IsScalar> + struct callable_arguments_impl; + + template<class F> + struct callable_arguments_impl<F, true> { + using function_member_pointer = typename scalar_run_member_pointer<F>::type; + using args_tuple = typename member_function_arguments<function_member_pointer>::tuple_type; + using return_type = typename member_function_arguments<function_member_pointer>::return_type; + }; + + template<class F> + struct callable_arguments_impl<F, false> { + using step_function_member_pointer = typename aggregate_run_member_pointer<F>::step_type; + using fin_function_member_pointer = typename aggregate_run_member_pointer<F>::fin_type; + using args_tuple = typename member_function_arguments<step_function_member_pointer>::tuple_type; + using return_type = typename member_function_arguments<fin_function_member_pointer>::return_type; + }; + + template<class F> + struct callable_arguments : callable_arguments_impl<F, is_scalar_function<F>::value> {}; + + template<class F, class... Args> + struct function_call { + using function_type = F; + using args_tuple = std::tuple<Args...>; + + args_tuple args; + }; + } + + /** + * Used to call user defined function: `func<MyFunc>(...);` + */ + template<class F, class... Args> + internal::function_call<F, Args...> func(Args... args) { + using args_tuple = std::tuple<Args...>; + using function_args_tuple = typename internal::callable_arguments<F>::args_tuple; + constexpr auto argsCount = std::tuple_size<args_tuple>::value; + constexpr auto functionArgsCount = std::tuple_size<function_args_tuple>::value; + static_assert( + (argsCount == functionArgsCount && !std::is_same<function_args_tuple, std::tuple<arg_values>>::value) || + std::is_same<function_args_tuple, std::tuple<arg_values>>::value, + "Arguments amount does not match"); + return {std::make_tuple(std::forward<Args>(args)...)}; + } + +} + +namespace sqlite_orm { namespace internal { @@ -5688,6 +8332,14 @@ namespace sqlite_orm { template<class St, class T, class SFINAE = void> struct column_result_t; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<class St, class T> + struct column_result_t<St, as_optional_t<T>, void> { + using type = std::optional<typename column_result_t<St, T>::type>; + }; + +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template<class St, class O, class F> struct column_result_t<St, F O::*, @@ -5696,6 +8348,16 @@ namespace sqlite_orm { using type = F; }; + template<class St, class L, class A> + struct column_result_t<St, dynamic_in_t<L, A>, void> { + using type = bool; + }; + + template<class St, class L, class... Args> + struct column_result_t<St, in_t<L, Args...>, void> { + using type = bool; + }; + /** * Common case for all getter types. Getter types are defined in column.h file */ @@ -5713,12 +8375,17 @@ namespace sqlite_orm { }; template<class St, class R, class S, class... Args> - struct column_result_t<St, internal::core_function_t<R, S, Args...>, void> { + struct column_result_t<St, built_in_function_t<R, S, Args...>, void> { using type = R; }; + template<class St, class F, class... Args> + struct column_result_t<St, function_call<F, Args...>, void> { + using type = typename callable_arguments<F>::return_type; + }; + template<class St, class X, class S> - struct column_result_t<St, core_function_t<internal::unique_ptr_result_of<X>, S, X>, void> { + struct column_result_t<St, built_in_function_t<internal::unique_ptr_result_of<X>, S, X>, void> { using type = std::unique_ptr<typename column_result_t<St, X>::type>; }; @@ -5854,6 +8521,11 @@ namespace sqlite_orm { using type = left_result; }; + template<class St, class T> + struct column_result_t<St, T, typename std::enable_if<is_base_of_template<T, binary_condition>::value>::type> { + using type = typename T::result_type; + }; + /** * Result for the most simple queries like `SELECT 1` */ @@ -5866,7 +8538,7 @@ namespace sqlite_orm { * Result for the most simple queries like `SELECT 'ototo'` */ template<class St> - struct column_result_t<St, const char *, void> { + struct column_result_t<St, const char*, void> { using type = std::string; }; @@ -5933,7 +8605,7 @@ namespace sqlite_orm { // #include "constraints.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "table_info.h" @@ -5945,44 +8617,41 @@ namespace sqlite_orm { namespace internal { - struct table_base { + struct basic_table { /** * Table name. */ std::string name; - - bool _without_rowid = false; }; /** - * Table interface class. Implementation is hidden in `table_impl` class. + * Table class. */ - template<class T, class... Cs> - struct table_t : table_base { + template<class T, bool WithoutRowId, class... Cs> + struct table_t : basic_table { + using super = basic_table; using object_type = T; - using columns_type = std::tuple<Cs...>; + using elements_type = std::tuple<Cs...>; - static constexpr const int columns_count = static_cast<int>(std::tuple_size<columns_type>::value); + static constexpr const int elements_count = static_cast<int>(std::tuple_size<elements_type>::value); + static constexpr const bool is_without_rowid = WithoutRowId; - columns_type columns; + elements_type elements; - table_t(decltype(name) name_, columns_type columns_) : - table_base{std::move(name_)}, columns(std::move(columns_)) {} + table_t(std::string name_, elements_type elements_) : super{move(name_)}, elements{move(elements_)} {} - table_t<T, Cs...> without_rowid() const { - auto res = *this; - res._without_rowid = true; - return res; + table_t<T, true, Cs...> without_rowid() const { + return {this->name, this->elements}; } /** * Function used to get field value from object by mapped member pointer/setter/getter */ template<class F, class C> - const F *get_object_field_pointer(const object_type &obj, C c) const { - const F *res = nullptr; - this->for_each_column_with_field_type<F>([&res, &c, &obj](auto &col) { + const F* get_object_field_pointer(const object_type& obj, C c) const { + const F* res = nullptr; + this->for_each_column_with_field_type<F>([&res, &c, &obj](auto& col) { using column_type = typename std::remove_reference<decltype(col)>::type; using member_pointer_t = typename column_type::member_pointer_t; using getter_type = typename column_type::getter_type; @@ -5990,21 +8659,21 @@ namespace sqlite_orm { // Make static_if have at least one input as a workaround for GCC bug: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 if(!res) { - static_if<std::is_same<C, member_pointer_t>{}>([&res, &obj, &col](const C &c_) { + static_if<std::is_same<C, member_pointer_t>{}>([&res, &obj, &col](const C& c_) { if(compare_any(col.member_pointer, c_)) { res = &(obj.*col.member_pointer); } })(c); } if(!res) { - static_if<std::is_same<C, getter_type>{}>([&res, &obj, &col](const C &c_) { + static_if<std::is_same<C, getter_type>{}>([&res, &obj, &col](const C& c_) { if(compare_any(col.getter, c_)) { res = &((obj).*(col.getter))(); } })(c); } if(!res) { - static_if<std::is_same<C, setter_type>{}>([&res, &obj, &col](const C &c_) { + static_if<std::is_same<C, setter_type>{}>([&res, &obj, &col](const C& c_) { if(compare_any(col.setter, c_)) { res = &((obj).*(col.getter))(); } @@ -6014,13 +8683,19 @@ namespace sqlite_orm { return res; } - /** - * @return vector of column names of table. - */ - std::vector<std::string> column_names() const { - std::vector<std::string> res; - this->for_each_column([&res](auto &c) { - res.push_back(c.name); + template<class C> + bool exists_in_composite_primary_key(const C& column) const { + auto res = false; + this->for_each_primary_key([&column, &res](auto& primaryKey) { + iterate_tuple(primaryKey.columns, [&res, &column](auto& value) { + if(!res) { + if(column.member_pointer) { + res = compare_any(value, column.member_pointer); + } else { + res = compare_any(value, column.getter) || compare_any(value, column.setter); + } + } + }); }); return res; } @@ -6029,16 +8704,16 @@ namespace sqlite_orm { * Calls **l** with every primary key dedicated constraint */ template<class L> - void for_each_primary_key(const L &l) const { - iterate_tuple(this->columns, [&l](auto &column) { - using column_type = typename std::decay<decltype(column)>::type; - static_if<internal::is_primary_key<column_type>{}>(l)(column); + void for_each_primary_key(const L& lambda) const { + iterate_tuple(this->elements, [&lambda](auto& element) { + using element_type = typename std::decay<decltype(element)>::type; + static_if<is_primary_key<element_type>{}>(lambda)(element); }); } std::vector<std::string> composite_key_columns_names() const { std::vector<std::string> res; - this->for_each_primary_key([this, &res](auto &c) { + this->for_each_primary_key([this, &res](auto& c) { res = this->composite_key_columns_names(c); }); return res; @@ -6046,7 +8721,7 @@ namespace sqlite_orm { std::vector<std::string> primary_key_column_names() const { std::vector<std::string> res; - this->for_each_column_with<constraints::primary_key_t<>>([&res](auto &c) { + this->for_each_column_with<primary_key_t<>>([&res](auto& c) { res.push_back(c.name); }); if(!res.size()) { @@ -6056,12 +8731,16 @@ namespace sqlite_orm { } template<class... Args> - std::vector<std::string> composite_key_columns_names(const constraints::primary_key_t<Args...> &pk) const { + std::vector<std::string> composite_key_columns_names(const primary_key_t<Args...>& pk) const { std::vector<std::string> res; using pk_columns_tuple = decltype(pk.columns); res.reserve(std::tuple_size<pk_columns_tuple>::value); - iterate_tuple(pk.columns, [this, &res](auto &v) { - res.push_back(this->find_column_name(v)); + iterate_tuple(pk.columns, [this, &res](auto& v) { + if(auto columnName = this->find_column_name(v)) { + res.push_back(*columnName); + } else { + res.push_back({}); + } }); return res; } @@ -6074,11 +8753,11 @@ namespace sqlite_orm { class O, typename = typename std::enable_if<std::is_member_pointer<F O::*>::value && !std::is_member_function_pointer<F O::*>::value>::type> - std::string find_column_name(F O::*m) const { - std::string res; - this->template for_each_column_with_field_type<F>([&res, m](auto &c) { + const std::string* find_column_name(F O::*m) const { + const std::string* res = nullptr; + this->template for_each_column_with_field_type<F>([&res, m](auto& c) { if(c.member_pointer == m) { - res = c.name; + res = &c.name; } }); return res; @@ -6089,13 +8768,13 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template<class G> - std::string find_column_name(G getter, - typename std::enable_if<is_getter<G>::value>::type * = nullptr) const { - std::string res; + const std::string* find_column_name(G getter, + typename std::enable_if<is_getter<G>::value>::type* = nullptr) const { + const std::string* res = nullptr; using field_type = typename getter_traits<G>::field_type; - this->template for_each_column_with_field_type<field_type>([&res, getter](auto &c) { + this->template for_each_column_with_field_type<field_type>([&res, getter](auto& c) { if(compare_any(c.getter, getter)) { - res = c.name; + res = &c.name; } }); return res; @@ -6106,39 +8785,55 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template<class S> - std::string find_column_name(S setter, - typename std::enable_if<is_setter<S>::value>::type * = nullptr) const { - std::string res; + const std::string* find_column_name(S setter, + typename std::enable_if<is_setter<S>::value>::type* = nullptr) const { + const std::string* res = nullptr; using field_type = typename setter_traits<S>::field_type; - this->template for_each_column_with_field_type<field_type>([&res, setter](auto &c) { + this->template for_each_column_with_field_type<field_type>([&res, setter](auto& c) { if(compare_any(c.setter, setter)) { - res = c.name; + res = &c.name; } }); return res; } + int count_columns_amount() const { + auto res = 0; + this->for_each_column([&res](auto&) { + ++res; + }); + return res; + } + /** - * Iterates all columns and fires passed lambda. Lambda must have one and only templated argument Otherwise - * code will not compile. Excludes table constraints (e.g. foreign_key_t) at the end of the columns list. To - * iterate columns with table constraints use iterate_tuple(columns, ...) instead. L is lambda type. Do - * not specify it explicitly. - * @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} + * Iterates all columns and fires passed lambda. Lambda must have one and only templated argument. Otherwise + * code will not compile. Excludes table constraints (e.g. foreign_key_t) at the end of the columns list. To + * iterate columns with table constraints use iterate_tuple(columns, ...) instead. L is lambda type. Do + * not specify it explicitly. + * @param lambda Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} */ template<class L> - void for_each_column(const L &l) const { - iterate_tuple(this->columns, [&l](auto &column) { - using column_type = typename std::decay<decltype(column)>::type; - static_if<is_column<column_type>{}>(l)(column); + void for_each_column(const L& lambda) const { + iterate_tuple(this->elements, [&lambda](auto& element) { + using element_type = typename std::decay<decltype(element)>::type; + static_if<is_column<element_type>{}>(lambda)(element); + }); + } + + template<class L> + void for_each_foreign_key(const L& lambda) const { + iterate_tuple(this->elements, [&lambda](auto& element) { + using element_type = typename std::decay<decltype(element)>::type; + static_if<is_foreign_key<element_type>{}>(lambda)(element); }); } template<class F, class L> - void for_each_column_with_field_type(const L &l) const { - iterate_tuple(this->columns, [&l](auto &column) { + void for_each_column_with_field_type(const L& lambda) const { + this->for_each_column([&lambda](auto& column) { using column_type = typename std::decay<decltype(column)>::type; using field_type = typename column_field_type<column_type>::type; - static_if<std::is_same<F, field_type>{}>(l)(column); + static_if<std::is_same<F, field_type>{}>(lambda)(column); }); } @@ -6146,22 +8841,22 @@ namespace sqlite_orm { * Iterates all columns that have specified constraints and fires passed lambda. * Lambda must have one and only templated argument Otherwise code will not compile. * L is lambda type. Do not specify it explicitly. - * @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} + * @param lambda Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} */ template<class Op, class L> - void for_each_column_with(const L &l) const { - using tuple_helper::tuple_contains_type; - iterate_tuple(this->columns, [&l](auto &column) { + void for_each_column_with(const L& lambda) const { + this->for_each_column([&lambda](auto& column) { + using tuple_helper::tuple_contains_type; using column_type = typename std::decay<decltype(column)>::type; using constraints_type = typename column_constraints_type<column_type>::type; - static_if<tuple_contains_type<Op, constraints_type>{}>(l)(column); + static_if<tuple_contains_type<Op, constraints_type>{}>(lambda)(column); }); } std::vector<table_info> get_table_info() const { std::vector<table_info> res; - res.reserve(size_t(this->columns_count)); - this->for_each_column([&res](auto &col) { + res.reserve(size_t(this->elements_count)); + this->for_each_column([&res](auto& col) { std::string dft; using field_type = typename std::decay<decltype(col)>::type::field_type; if(auto d = col.default_value()) { @@ -6173,14 +8868,14 @@ namespace sqlite_orm { type_printer<field_type>().print(), col.not_null(), dft, - col.template has<constraints::primary_key_t<>>(), + col.template has<primary_key_t<>>(), }; res.emplace_back(i); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { - auto &columnName = compositeKeyColumnNames[i]; - auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_info &ti) { + auto& columnName = compositeKeyColumnNames[i]; + auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_info& ti) { return ti.name == columnName; }); if(it != res.end()) { @@ -6197,12 +8892,12 @@ namespace sqlite_orm { * cause table class is templated and its constructing too (just like std::make_unique or std::make_pair). */ template<class... Cs, class T = typename std::tuple_element<0, std::tuple<Cs...>>::type::object_type> - internal::table_t<T, Cs...> make_table(const std::string &name, Cs... args) { + internal::table_t<T, false, Cs...> make_table(const std::string& name, Cs... args) { return {name, std::make_tuple<Cs...>(std::forward<Cs>(args)...)}; } template<class T, class... Cs> - internal::table_t<T, Cs...> make_table(const std::string &name, Cs... args) { + internal::table_t<T, false, Cs...> make_table(const std::string& name, Cs... args) { return {name, std::make_tuple<Cs...>(std::forward<Cs>(args)...)}; } } @@ -6213,7 +8908,7 @@ namespace sqlite_orm { #include <cstddef> // std::nullptr_t #include <system_error> // std::system_error, std::error_code #include <sstream> // std::stringstream -#include <cstdlib> // std::atoi +#include <stdlib.h> // std::atoi #include <type_traits> // std::forward, std::enable_if, std::is_same, std::remove_reference, std::false_type, std::true_type #include <utility> // std::pair, std::make_pair #include <vector> // std::vector @@ -6226,6 +8921,8 @@ namespace sqlite_orm { // #include "row_extractor.h" +// #include "util.h" + // #include "constraints.h" // #include "select_constraints.h" @@ -6252,7 +8949,7 @@ namespace sqlite_orm { struct field_value_holder<T, typename std::enable_if<getter_traits<T>::returns_lvalue>::type> { using type = typename getter_traits<T>::field_type; - const type &value; + const type& value; }; template<class T> @@ -6270,7 +8967,7 @@ namespace sqlite_orm { struct storage_impl_base { - bool table_exists(const std::string &tableName, sqlite3 *db) const { + bool table_exists(const std::string& tableName, sqlite3* db) const { auto result = false; std::stringstream ss; ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '" @@ -6280,8 +8977,8 @@ namespace sqlite_orm { auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char * * /*azColName*/) -> int { - auto &res = *(bool *)data; + [](void* data, int argc, char** argv, char** /*azColName*/) -> int { + auto& res = *(bool*)data; if(argc) { res = !!std::atoi(argv[0]); } @@ -6296,28 +8993,15 @@ namespace sqlite_orm { return result; } - void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) const { + void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { std::stringstream ss; ss << "ALTER TABLE " << oldName << " RENAME TO " << newName; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(db, ss.str()); } - static bool get_remove_add_columns(std::vector<table_info *> &columnsToAdd, - std::vector<table_info> &storageTableInfo, - std::vector<table_info> &dbTableInfo) { + static bool calculate_remove_add_columns(std::vector<table_info*>& columnsToAdd, + std::vector<table_info>& storageTableInfo, + std::vector<table_info>& dbTableInfo) { bool notEqual = false; // iterate through storage columns @@ -6325,15 +9009,15 @@ namespace sqlite_orm { ++storageColumnInfoIndex) { // get storage's column info - auto &storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; - auto &columnName = storageColumnInfo.name; + auto& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; + auto& columnName = storageColumnInfo.name; // search for a column in db eith the same name - auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto &ti) { + auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { return ti.name == columnName; }); if(dbColumnInfoIt != dbTableInfo.end()) { - auto &dbColumnInfo = *dbColumnInfoIt; + auto& dbColumnInfo = *dbColumnInfoIt; auto columnsAreEqual = dbColumnInfo.name == storageColumnInfo.name && dbColumnInfo.notnull == storageColumnInfo.notnull && @@ -6354,14 +9038,14 @@ namespace sqlite_orm { return notEqual; } - std::vector<table_info> get_table_info(const std::string &tableName, sqlite3 *db) const { + std::vector<table_info> get_table_info(const std::string& tableName, sqlite3* db) const { std::vector<table_info> result; auto query = "PRAGMA table_info('" + tableName + "')"; auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(std::vector<table_info> *)data; + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector<table_info>*)data; if(argc) { auto index = 0; auto cid = std::atoi(argv[index++]); @@ -6371,7 +9055,7 @@ namespace sqlite_orm { std::string dflt_value = argv[index] ? argv[index] : ""; index++; auto pk = std::atoi(argv[index++]); - res.push_back(table_info{cid, name, type, notnull, dflt_value, pk}); + res.push_back(table_info(cid, name, type, notnull, dflt_value, pk)); } return 0; }, @@ -6401,7 +9085,7 @@ namespace sqlite_orm { table_type table; template<class L> - void for_each(const L &l) { + void for_each(const L& l) { this->super::for_each(l); l(*this); } @@ -6413,8 +9097,8 @@ namespace sqlite_orm { */ int foreign_keys_count() { auto res = 0; - iterate_tuple(this->table.columns, [&res](auto &c) { - if(internal::is_foreign_key<typename std::decay<decltype(c)>::type>::value) { + iterate_tuple(this->table.elements, [&res](auto& c) { + if(is_foreign_key<typename std::decay<decltype(c)>::type>::value) { ++res; } }); @@ -6429,7 +9113,7 @@ namespace sqlite_orm { * `column_name` has SFINAE check for type equality but `column_name_simple` has not. */ template<class O, class F> - std::string column_name_simple(F O::*m) const { + const std::string* column_name_simple(F O::*m) const { return this->table.find_column_name(m); } @@ -6438,8 +9122,8 @@ namespace sqlite_orm { * skip inequal type O. */ template<class O, class F, class HH = typename H::object_type> - std::string column_name(F O::*m, - typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) const { + const std::string* column_name(F O::*m, + typename std::enable_if<std::is_same<O, HH>::value>::type* = nullptr) const { return this->table.find_column_name(m); } @@ -6447,50 +9131,51 @@ namespace sqlite_orm { * Opposite version of function defined above. Just calls same function in superclass. */ template<class O, class F, class HH = typename H::object_type> - std::string column_name(F O::*m, - typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) const { + const std::string* + column_name(F O::*m, typename std::enable_if<!std::is_same<O, HH>::value>::type* = nullptr) const { return this->super::column_name(m); } template<class T, class F, class HH = typename H::object_type> - std::string column_name(const column_pointer<T, F> &c, - typename std::enable_if<std::is_same<T, HH>::value>::type * = nullptr) const { + const std::string* column_name(const column_pointer<T, F>& c, + typename std::enable_if<std::is_same<T, HH>::value>::type* = nullptr) const { return this->column_name_simple(c.field); } template<class T, class F, class HH = typename H::object_type> - std::string column_name(const column_pointer<T, F> &c, - typename std::enable_if<!std::is_same<T, HH>::value>::type * = nullptr) const { + const std::string* + column_name(const column_pointer<T, F>& c, + typename std::enable_if<!std::is_same<T, HH>::value>::type* = nullptr) const { return this->super::column_name(c); } template<class O, class HH = typename H::object_type> - const auto &get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) const { + const auto& get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type* = nullptr) const { return *this; } template<class O, class HH = typename H::object_type> - const auto &get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) const { + const auto& get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type* = nullptr) const { return this->super::template get_impl<O>(); } template<class O, class HH = typename H::object_type> - auto &get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) { + auto& get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type* = nullptr) { return *this; } template<class O, class HH = typename H::object_type> - auto &get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) { + auto& get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type* = nullptr) { return this->super::template get_impl<O>(); } template<class O, class HH = typename H::object_type> - const auto *find_table(typename std::enable_if<std::is_same<O, HH>::value>::type * = nullptr) const { + const auto* find_table(typename std::enable_if<std::is_same<O, HH>::value>::type* = nullptr) const { return &this->table; } template<class O, class HH = typename H::object_type> - const auto *find_table(typename std::enable_if<!std::is_same<O, HH>::value>::type * = nullptr) const { + const auto* find_table(typename std::enable_if<!std::is_same<O, HH>::value>::type* = nullptr) const { return this->super::template find_table<O>(); } @@ -6503,34 +9188,20 @@ namespace sqlite_orm { } } - void add_column(const table_info &ti, sqlite3 *db) const { + void add_column(const table_info& ti, sqlite3* db) const { std::stringstream ss; - ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name << " "; - ss << ti.type << " "; + ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name; + ss << " " << ti.type; if(ti.pk) { - ss << "PRIMARY KEY "; + ss << " PRIMARY KEY"; } if(ti.notnull) { - ss << "NOT NULL "; + ss << " NOT NULL"; } if(ti.dflt_value.length()) { - ss << "DEFAULT " << ti.dflt_value << " "; - } - auto query = ss.str(); - sqlite3_stmt *stmt; - auto prepareResult = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); - if(prepareResult == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + ss << " DEFAULT " << ti.dflt_value; } + perform_void_exec(db, ss.str()); } /** @@ -6538,13 +9209,11 @@ namespace sqlite_orm { * Performs CREATE TABLE %name% AS SELECT %this->table.columns_names()% FROM &this->table.name%; */ void - copy_table(sqlite3 *db, const std::string &name, const std::vector<table_info *> &columnsToIgnore) const { - std::ignore = columnsToIgnore; - + copy_table(sqlite3* db, const std::string& name, const std::vector<table_info*>& columnsToIgnore) const { std::stringstream ss; std::vector<std::string> columnNames; - this->table.for_each_column([&columnNames, &columnsToIgnore](auto &c) { - auto &columnName = c.name; + this->table.for_each_column([&columnNames, &columnsToIgnore](auto& c) { + auto& columnName = c.name; auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), columnsToIgnore.end(), [&columnName](auto tableInfoPointer) { @@ -6568,28 +9237,14 @@ namespace sqlite_orm { for(size_t i = 0; i < columnNamesCount; ++i) { ss << columnNames[i]; if(i < columnNamesCount - 1) { - ss << ","; - } - ss << " "; - } - ss << "FROM '" << this->table.name << "' "; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + ss << ", "; } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); } + ss << " FROM '" << this->table.name << "' "; + perform_void_exec(db, ss.str()); } - sync_schema_result schema_status(sqlite3 *db, bool preserve) const { + sync_schema_result schema_status(sqlite3* db, bool preserve) const { auto res = sync_schema_result::already_in_sync; @@ -6604,9 +9259,9 @@ namespace sqlite_orm { auto dbTableInfo = this->get_table_info(this->table.name, db); // this vector will contain pointers to columns that gotta be added.. - std::vector<table_info *> columnsToAdd; + std::vector<table_info*> columnsToAdd; - if(this->get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { + if(this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { gottaCreateTable = true; } @@ -6665,14 +9320,14 @@ namespace sqlite_orm { } template<class L> - void for_each(const L &) {} + void for_each(const L&) {} int foreign_keys_count() { return 0; } template<class O> - const void *find_table() const { + const void* find_table() const { return nullptr; } }; @@ -6725,7 +9380,7 @@ namespace sqlite_orm { namespace internal { struct object_from_column_builder_base { - sqlite3_stmt *stmt = nullptr; + sqlite3_stmt* stmt = nullptr; mutable int index = 0; }; @@ -6736,13 +9391,13 @@ namespace sqlite_orm { struct object_from_column_builder : object_from_column_builder_base { using object_type = O; - object_type &object; + object_type& object; - object_from_column_builder(object_type &object_, sqlite3_stmt *stmt_) : + object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_) : object_from_column_builder_base{stmt_}, object(object_) {} template<class C> - void operator()(const C &c) const { + void operator()(const C& c) const { using field_type = typename C::field_type; auto value = row_extractor<field_type>().extract(this->stmt, this->index++); if(c.member_pointer) { @@ -6761,27 +9416,27 @@ namespace sqlite_orm { namespace internal { /** - * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. - * Main difference from regular `row_extractor` is that this class takes table info which is required - * for constructing objects by member pointers. To construct please use `row_extractor_builder` class - * Type arguments: - * V is value type just like regular `row_extractor` has - * T is table info class `table_t` - */ + * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. + * Main difference from regular `row_extractor` is that this class takes table info which is required + * for constructing objects by member pointers. To construct please use `row_extractor_builder` class + * Type arguments: + * V is value type just like regular `row_extractor` has + * T is table info class `table_t` + */ template<class V, class T> struct mapped_row_extractor { using table_info_t = T; - mapped_row_extractor(const table_info_t &tableInfo_) : tableInfo(tableInfo_) {} + mapped_row_extractor(const table_info_t& tableInfo_) : tableInfo(tableInfo_) {} - V extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + V extract(sqlite3_stmt* stmt, int /*columnIndex*/) { V res; object_from_column_builder<V> builder{res, stmt}; this->tableInfo.for_each_column(builder); return res; } - const table_info_t &tableInfo; + const table_info_t& tableInfo; }; } @@ -6793,18 +9448,18 @@ namespace sqlite_orm { namespace internal { /** - * This builder is used to construct different row extractors depending on type. - * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and - * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns - * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. - */ + * This builder is used to construct different row extractors depending on type. + * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and + * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns + * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. + */ template<class T, bool IsMapped, class I> struct row_extractor_builder; template<class T, class I> struct row_extractor_builder<T, false, I> { - row_extractor<T> operator()(const I * /*tableInfo*/) const { + row_extractor<T> operator()(const I* /*tableInfo*/) const { return {}; } }; @@ -6812,13 +9467,13 @@ namespace sqlite_orm { template<class T, class I> struct row_extractor_builder<T, true, I> { - mapped_row_extractor<T, I> operator()(const I *tableInfo) const { + mapped_row_extractor<T, I> operator()(const I* tableInfo) const { return {*tableInfo}; } }; template<class T, bool IsMapped, class I> - auto make_row_extractor(const I *tableInfo) { + auto make_row_extractor(const I* tableInfo) { using builder_t = row_extractor_builder<T, IsMapped, I>; return builder_t{}(tableInfo); } @@ -6831,7 +9486,7 @@ namespace sqlite_orm { // #include "type_printer.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "constraints.h" @@ -6915,8 +9570,10 @@ namespace sqlite_orm { * call. When one finishes iterating it the pointer * inside the shared_ptr is nulled out in all copies. */ - std::shared_ptr<sqlite3_stmt *> stmt; - view_type &view; + std::shared_ptr<statement_finalizer> stmt; + + // only null for the default constructed iterator + view_type* view; /** * shared_ptr is used over unique_ptr here @@ -6924,68 +9581,53 @@ namespace sqlite_orm { */ std::shared_ptr<value_type> current; - void extract_value(std::unique_ptr<value_type> &temp) { - temp = std::make_unique<value_type>(); - auto &storage = this->view.storage; - auto &impl = storage.template get_impl<value_type>(); - object_from_column_builder<value_type> builder{*temp, *this->stmt}; + void extract_value() { + auto& storage = this->view->storage; + auto& impl = storage.template get_impl<value_type>(); + this->current = std::make_shared<value_type>(); + object_from_column_builder<value_type> builder{*this->current, this->stmt->get()}; impl.table.for_each_column(builder); } public: using difference_type = std::ptrdiff_t; - using pointer = value_type *; - using reference = value_type &; + using pointer = value_type*; + using reference = value_type&; using iterator_category = std::input_iterator_tag; - iterator_t(sqlite3_stmt *stmt_, view_type &view_) : - stmt(std::make_shared<sqlite3_stmt *>(stmt_)), view(view_) { - this->operator++(); - } - - iterator_t(const iterator_t &) = default; - - iterator_t(iterator_t &&) = default; - - iterator_t &operator=(iterator_t &&) = default; + iterator_t() : view(nullptr){}; - iterator_t &operator=(const iterator_t &) = default; - - ~iterator_t() { - if(this->stmt) { - statement_finalizer f{*this->stmt}; - } + iterator_t(sqlite3_stmt* stmt_, view_type& view_) : + stmt(std::make_shared<statement_finalizer>(stmt_)), view(&view_) { + next(); } - value_type &operator*() { - if(!this->stmt) { + const value_type& operator*() const { + if(!this->stmt || !this->current) { throw std::system_error(std::make_error_code(orm_error_code::trying_to_dereference_null_iterator)); } - if(!this->current) { - std::unique_ptr<value_type> value; - this->extract_value(value); - this->current = move(value); - } return *this->current; } - value_type *operator->() { + const value_type* operator->() const { return &(this->operator*()); } - void operator++() { - if(this->stmt && *this->stmt) { - auto ret = sqlite3_step(*this->stmt); + private: + void next() { + this->current.reset(); + if(this->stmt) { + auto statementPointer = this->stmt->get(); + auto ret = sqlite3_step(statementPointer); switch(ret) { case SQLITE_ROW: - this->current = nullptr; + this->extract_value(); + break; + case SQLITE_DONE: + this->stmt.reset(); break; - case SQLITE_DONE: { - statement_finalizer f{*this->stmt}; - *this->stmt = nullptr; - } break; default: { - auto db = this->view.connection.get(); + auto db = this->view->connection.get(); throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } @@ -6993,23 +9635,21 @@ namespace sqlite_orm { } } + public: + iterator_t<V>& operator++() { + next(); + return *this; + } + void operator++(int) { this->operator++(); } - bool operator==(const iterator_t &other) const { - if(this->stmt && other.stmt) { - return *this->stmt == *other.stmt; - } else { - if(!this->stmt && !other.stmt) { - return true; - } else { - return false; - } - } + bool operator==(const iterator_t& other) const { + return this->current == other.current; } - bool operator!=(const iterator_t &other) const { + bool operator!=(const iterator_t& other) const { return !(*this == other); } }; @@ -7027,7 +9667,7 @@ namespace sqlite_orm { // #include "operators.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "core_functions.h" @@ -7077,7 +9717,7 @@ namespace sqlite_orm { } } - sqlite3 *get() const { + sqlite3* get() const { return this->db; } @@ -7088,20 +9728,20 @@ namespace sqlite_orm { const std::string filename; protected: - sqlite3 *db = nullptr; + sqlite3* db = nullptr; int _retain_count = 0; }; struct connection_ref { - connection_ref(connection_holder &holder_) : holder(holder_) { + connection_ref(connection_holder& holder_) : holder(holder_) { this->holder.retain(); } - connection_ref(const connection_ref &other) : holder(other.holder) { + connection_ref(const connection_ref& other) : holder(other.holder) { this->holder.retain(); } - connection_ref(connection_ref &&other) : holder(other.holder) { + connection_ref(connection_ref&& other) : holder(other.holder) { this->holder.retain(); } @@ -7109,24 +9749,67 @@ namespace sqlite_orm { this->holder.release(); } - sqlite3 *get() const { + sqlite3* get() const { return this->holder.get(); } protected: - connection_holder &holder; + connection_holder& holder; }; } } // #include "select_constraints.h" +// #include "values.h" + +#include <vector> // std::vector +#include <initializer_list> +#include <tuple> // std::tuple +#include <type_traits> // std::false_type, std::true_type + +namespace sqlite_orm { + + namespace internal { + + template<class... Args> + struct values_t { + using args_tuple = std::tuple<Args...>; + + args_tuple tuple; + }; + + template<class T> + struct is_values : std::false_type {}; + + template<class... Args> + struct is_values<values_t<Args...>> : std::true_type {}; + + template<class T> + struct dynamic_values_t { + std::vector<T> vector; + }; + + } + + template<class... Args> + internal::values_t<Args...> values(Args... args) { + return {{std::forward<Args>(args)...}}; + } + + template<class T> + internal::dynamic_values_t<T> values(std::vector<T> vector) { + return {{move(vector)}}; + } + +} + namespace sqlite_orm { namespace internal { struct prepared_statement_base { - sqlite3_stmt *stmt = nullptr; + sqlite3_stmt* stmt = nullptr; connection_ref con; ~prepared_statement_base() { @@ -7184,7 +9867,7 @@ namespace sqlite_orm { expression_type t; - prepared_statement_t(T t_, sqlite3_stmt *stmt_, connection_ref con_) : + prepared_statement_t(T t_, sqlite3_stmt* stmt_, connection_ref con_) : prepared_statement_base{stmt_, std::move(con_)}, t(std::move(t_)) {} }; @@ -7297,6 +9980,12 @@ namespace sqlite_orm { type obj; }; + template<class T> + struct is_insert : std::false_type {}; + + template<class T> + struct is_insert<insert_t<T>> : std::true_type {}; + template<class T, class... Cols> struct insert_explicit { using type = T; @@ -7313,40 +10002,358 @@ namespace sqlite_orm { type obj; }; - template<class It> + template<class T> + struct is_replace : std::false_type {}; + + template<class T> + struct is_replace<replace_t<T>> : std::true_type {}; + + template<class It, class L, class O> struct insert_range_t { using iterator_type = It; - using object_type = typename std::iterator_traits<iterator_type>::value_type; + using container_object_type = typename std::iterator_traits<iterator_type>::value_type; + using transformer_type = L; + using object_type = O; std::pair<iterator_type, iterator_type> range; + transformer_type transformer; }; - template<class It> + template<class T> + struct is_insert_range : std::false_type {}; + + template<class It, class L, class O> + struct is_insert_range<insert_range_t<It, L, O>> : std::true_type {}; + + template<class It, class L, class O> struct replace_range_t { using iterator_type = It; - using object_type = typename std::iterator_traits<iterator_type>::value_type; + using container_object_type = typename std::iterator_traits<iterator_type>::value_type; + using transformer_type = L; + using object_type = O; std::pair<iterator_type, iterator_type> range; + transformer_type transformer; }; + + template<class T> + struct is_replace_range : std::false_type {}; + + template<class It, class L, class O> + struct is_replace_range<replace_range_t<It, L, O>> : std::true_type {}; + + template<class... Args> + struct insert_raw_t { + using args_tuple = std::tuple<Args...>; + + args_tuple args; + }; + + template<class T> + struct is_insert_raw : std::false_type {}; + + template<class... Args> + struct is_insert_raw<insert_raw_t<Args...>> : std::true_type {}; + + template<class... Args> + struct replace_raw_t { + using args_tuple = std::tuple<Args...>; + + args_tuple args; + }; + + template<class T> + struct is_replace_raw : std::false_type {}; + + template<class... Args> + struct is_replace_raw<replace_raw_t<Args...>> : std::true_type {}; + + template<class T> + struct into_t { + using type = T; + }; + + template<class T> + struct is_into : std::false_type {}; + + template<class T> + struct is_into<into_t<T>> : std::true_type {}; + + struct default_transformer { + + template<class T> + const T& operator()(const T& object) const { + return object; + } + }; + + struct default_values_t {}; + + template<class T> + using is_default_values = std::is_same<default_values_t, T>; + + enum class insert_constraint { + abort, + fail, + ignore, + replace, + rollback, + }; + + template<class T> + using is_insert_constraint = std::is_same<insert_constraint, T>; + + template<class T> + struct is_upsert_clause; + } + + inline internal::insert_constraint or_rollback() { + return internal::insert_constraint::rollback; + } + + inline internal::insert_constraint or_replace() { + return internal::insert_constraint::replace; + } + + inline internal::insert_constraint or_ignore() { + return internal::insert_constraint::ignore; + } + + inline internal::insert_constraint or_fail() { + return internal::insert_constraint::fail; + } + + inline internal::insert_constraint or_abort() { + return internal::insert_constraint::abort; + } + + /** + * Use this function to add `DEFAULT VALUES` modifier to raw `INSERT`. + * + * @example + * ``` + * storage.insert(into<Singer>(), default_values()); + * ``` + */ + inline internal::default_values_t default_values() { + return {}; + } + + template<class T> + internal::into_t<T> into() { + return {}; + } + + /** + * Raw insert statement creation routine. Use this if `insert` with object does not fit you. This insert is designed to be able + * to call any type of `INSERT` query with no limitations. + * @example + * ```sql + * INSERT INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into<User>, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * storage.execute(statement)); + * ``` + * One more example: + * ```sql + * INSERT INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into<Singer>(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * storage.execute(statement)); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * INSERT INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into<Singer>(), default_values())); + * storage.execute(statement)); + * ``` + * Also one can use `INSERT OR ABORT`/`INSERT OR FAIL`/`INSERT OR IGNORE`/`INSERT OR REPLACE`/`INSERT ROLLBACK`: + * ```c++ + * auto statement = storage.prepare(insert(or_ignore(), into<Singer>(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * auto statement2 = storage.prepare(insert(or_rollback(), into<Singer>(), default_values())); + * auto statement3 = storage.prepare(insert(or_abort(), into<User>, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * ``` + */ + template<class... Args> + internal::insert_raw_t<Args...> insert(Args... args) { + using args_tuple = std::tuple<Args...>; + using internal::count_tuple; + using internal::is_columns; + using internal::is_insert_constraint; + using internal::is_into; + using internal::is_select; + using internal::is_upsert_clause; + using internal::is_values; + + constexpr int orArgsCount = count_tuple<args_tuple, is_insert_constraint>::value; + static_assert(orArgsCount < 2, "Raw insert must have only one OR... argument"); + + constexpr int intoArgsCount = count_tuple<args_tuple, is_into>::value; + static_assert(intoArgsCount != 0, "Raw insert must have into<T> argument"); + static_assert(intoArgsCount < 2, "Raw insert must have only one into<T> argument"); + + constexpr int columnsArgsCount = count_tuple<args_tuple, is_columns>::value; + static_assert(columnsArgsCount < 2, "Raw insert must have only one columns(...) argument"); + + constexpr int valuesArgsCount = count_tuple<args_tuple, is_values>::value; + static_assert(valuesArgsCount < 2, "Raw insert must have only one values(...) argument"); + + constexpr int defaultValuesCount = count_tuple<args_tuple, internal::is_default_values>::value; + static_assert(defaultValuesCount < 2, "Raw insert must have only one default_values() argument"); + + constexpr int selectsArgsCount = count_tuple<args_tuple, is_select>::value; + static_assert(selectsArgsCount < 2, "Raw insert must have only one select(...) argument"); + + constexpr int upsertClausesCount = count_tuple<args_tuple, is_upsert_clause>::value; + static_assert(upsertClausesCount <= 2, "Raw insert can contain 2 instances of upsert clause maximum"); + + constexpr int argsCount = int(std::tuple_size<args_tuple>::value); + static_assert(argsCount == intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + + selectsArgsCount + orArgsCount + upsertClausesCount, + "Raw insert has invalid arguments"); + + return {{std::forward<Args>(args)...}}; + } + + /** + * Raw replace statement creation routine. Use this if `replace` with object does not fit you. This replace is designed to be able + * to call any type of `REPLACE` query with no limitations. Actually this is the same query as raw insert except `OR...` option existance. + * @example + * ```sql + * REPLACE INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into<User>, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * storage.execute(statement)); + * ``` + * One more example: + * ```sql + * REPLACE INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into<Singer>(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * storage.execute(statement)); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * REPLACE INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into<Singer>(), default_values())); + * storage.execute(statement)); + * ``` + */ + template<class... Args> + internal::replace_raw_t<Args...> replace(Args... args) { + using args_tuple = std::tuple<Args...>; + using internal::count_tuple; + using internal::is_columns; + using internal::is_into; + using internal::is_values; + + constexpr int intoArgsCount = count_tuple<args_tuple, is_into>::value; + static_assert(intoArgsCount != 0, "Raw replace must have into<T> argument"); + static_assert(intoArgsCount < 2, "Raw replace must have only one into<T> argument"); + + constexpr int columnsArgsCount = count_tuple<args_tuple, is_columns>::value; + static_assert(columnsArgsCount < 2, "Raw replace must have only one columns(...) argument"); + + constexpr int valuesArgsCount = count_tuple<args_tuple, is_values>::value; + static_assert(valuesArgsCount < 2, "Raw replace must have only one values(...) argument"); + + constexpr int defaultValuesCount = count_tuple<args_tuple, internal::is_default_values>::value; + static_assert(defaultValuesCount < 2, "Raw replace must have only one default_values() argument"); + + constexpr int selectsArgsCount = count_tuple<args_tuple, internal::is_select>::value; + static_assert(selectsArgsCount < 2, "Raw replace must have only one select(...) argument"); + + constexpr int argsCount = int(std::tuple_size<args_tuple>::value); + static_assert(argsCount == + intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + selectsArgsCount, + "Raw replace has invalid arguments"); + + return {{std::forward<Args>(args)...}}; } /** * Create a replace range statement + * + * @example + * ``` + * std::vector<User> users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(replace_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` */ template<class It> - internal::replace_range_t<It> replace_range(It from, It to) { + internal::replace_range_t<It, internal::default_transformer, typename std::iterator_traits<It>::value_type> + replace_range(It from, It to) { return {{std::move(from), std::move(to)}}; } /** + * Create an replace range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, + * optionals or whatever. + * @example + * ``` + * std::vector<std::unique_ptr<User>> userPointers; + * userPointers.push_back(std::make_unique<User>(1, "Eneli")); + * auto statement = storage.prepare(replace_range<User>(userPointers.begin(), userPointers.end(), [](const std::unique_ptr<User> &userPointer) -> const User & { + * return *userPointer; + * })); + * storage.execute(statement); + * ``` + */ + template<class T, class It, class L> + internal::replace_range_t<It, L, T> replace_range(It from, It to, L transformer) { + return {{std::move(from), std::move(to)}, std::move(transformer)}; + } + + /** * Create an insert range statement + * @example + * ``` + * std::vector<User> users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(insert_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` */ template<class It> - internal::insert_range_t<It> insert_range(It from, It to) { - return {{std::move(from), std::move(to)}}; + internal::insert_range_t<It, internal::default_transformer, typename std::iterator_traits<It>::value_type> + insert_range(It from, It to) { + return {{std::move(from), std::move(to)}, internal::default_transformer{}}; } /** + * Create an insert range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, + * optionals or whatever. + * @example + * ``` + * std::vector<std::unique_ptr<User>> userPointers; + * userPointers.push_back(std::make_unique<User>(1, "Eneli")); + * auto statement = storage.prepare(insert_range<User>(userPointers.begin(), userPointers.end(), [](const std::unique_ptr<User> &userPointer) -> const User & { + * return *userPointer; + * })); + * storage.execute(statement); + * ``` + */ + template<class T, class It, class L> + internal::insert_range_t<It, L, T> insert_range(It from, It to, L transformer) { + return {{std::move(from), std::move(to)}, std::move(transformer)}; + } + /** * Create a replace statement. * T is an object type mapped to a storage. * Usage: storage.replace(myUserInstance); @@ -7551,38 +10558,91 @@ namespace sqlite_orm { // #include "values.h" -#include <vector> // std::vector -#include <initializer_list> -#include <tuple> // std::tuple +// #include "function.h" + +// #include "ast/excluded.h" namespace sqlite_orm { + namespace internal { + + template<class T> + struct excluded_t { + using expression_type = T; + + expression_type expression; + }; + } + + template<class T> + internal::excluded_t<T> excluded(T expression) { + return {std::move(expression)}; + } +} +// #include "ast/upsert_clause.h" + +#include <tuple> // std::tuple +#include <type_traits> // std::false_type, std::true_type + +namespace sqlite_orm { namespace internal { + template<class T, class A> + struct upsert_clause; + template<class... Args> - struct values_t { - std::tuple<Args...> tuple; + struct conflict_target { + using args_tuple = std::tuple<Args...>; + + args_tuple args; + + upsert_clause<args_tuple, std::tuple<>> do_nothing() { + return {std::move(this->args), {}}; + } + + template<class... ActionsArgs> + upsert_clause<args_tuple, std::tuple<ActionsArgs...>> do_update(ActionsArgs... actions) { + return {std::move(this->args), {std::make_tuple(std::forward<ActionsArgs>(actions)...)}}; + } }; - template<class T> - struct dynamic_values_t { - std::vector<T> vector; + template<class... TargetArgs, class... ActionsArgs> + struct upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>> { + using target_args_tuple = std::tuple<TargetArgs...>; + using actions_tuple = std::tuple<ActionsArgs...>; + + target_args_tuple target_args; + + actions_tuple actions; }; - } + template<class T> + struct is_upsert_clause : std::false_type {}; - template<class... Args> - internal::values_t<Args...> values(Args... args) { - return {{std::forward<Args>(args)...}}; + template<class T, class A> + struct is_upsert_clause<upsert_clause<T, A>> : std::true_type {}; } - template<class T> - internal::dynamic_values_t<T> values(std::vector<T> vector) { - return {{move(vector)}}; + /** + * ON CONFLICT upsert clause builder function. + * @example + * storage.insert(into<Employee>(), + * columns(&Employee::id, &Employee::name, &Employee::age, &Employee::address, &Employee::salary), + * values(std::make_tuple(3, "Sofia", 26, "Madrid", 15000.0), + * std::make_tuple(4, "Doja", 26, "LA", 25000.0)), + * on_conflict(&Employee::id).do_update(set(c(&Employee::name) = excluded(&Employee::name), + * c(&Employee::age) = excluded(&Employee::age), + * c(&Employee::address) = excluded(&Employee::address), + * c(&Employee::salary) = excluded(&Employee::salary)))); + */ + template<class... Args> + internal::conflict_target<Args...> on_conflict(Args... args) { + return {std::tuple<Args...>(std::forward<Args>(args)...)}; } - } +// #include "ast/where.h" + namespace sqlite_orm { namespace internal { @@ -7604,7 +10664,7 @@ namespace sqlite_orm { * L is a callable type. Mostly is a templated lambda */ template<class L> - void operator()(const T &t, const L &l) const { + void operator()(const T& t, const L& l) const { l(t); } }; @@ -7613,18 +10673,50 @@ namespace sqlite_orm { * Simplified API */ template<class T, class L> - void iterate_ast(const T &t, const L &l) { + void iterate_ast(const T& t, const L& l) { ast_iterator<T> iterator; iterator(t, l); } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<class T> + struct ast_iterator<as_optional_t<T>, void> { + using node_type = as_optional_t<T>; + + template<class L> + void operator()(const node_type& node, const L& lambda) const { + iterate_ast(node.value, lambda); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template<class T> struct ast_iterator<std::reference_wrapper<T>, void> { using node_type = std::reference_wrapper<T>; template<class L> - void operator()(const node_type &r, const L &l) const { - iterate_ast(r.get(), l); + void operator()(const node_type& r, const L& lambda) const { + iterate_ast(r.get(), lambda); + } + }; + + template<class T> + struct ast_iterator<excluded_t<T>, void> { + using node_type = excluded_t<T>; + + template<class L> + void operator()(const node_type& expression, const L& lambda) const { + iterate_ast(expression.expression, lambda); + } + }; + + template<class... TargetArgs, class... ActionsArgs> + struct ast_iterator<upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>, void> { + using node_type = upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>; + + template<class L> + void operator()(const node_type& expression, const L& lambda) const { + iterate_ast(expression.actions, lambda); } }; @@ -7633,8 +10725,8 @@ namespace sqlite_orm { using node_type = where_t<C>; template<class L> - void operator()(const node_type &where, const L &l) const { - iterate_ast(where.c, l); + void operator()(const node_type& expression, const L& lambda) const { + iterate_ast(expression.expression, lambda); } }; @@ -7643,7 +10735,7 @@ namespace sqlite_orm { using node_type = T; template<class L> - void operator()(const node_type &binaryCondition, const L &l) const { + void operator()(const node_type& binaryCondition, const L& l) const { iterate_ast(binaryCondition.l, l); iterate_ast(binaryCondition.r, l); } @@ -7654,7 +10746,7 @@ namespace sqlite_orm { using node_type = binary_operator<L, R, Ds...>; template<class C> - void operator()(const node_type &binaryOperator, const C &l) const { + void operator()(const node_type& binaryOperator, const C& l) const { iterate_ast(binaryOperator.lhs, l); iterate_ast(binaryOperator.rhs, l); } @@ -7665,19 +10757,30 @@ namespace sqlite_orm { using node_type = columns_t<Args...>; template<class L> - void operator()(const node_type &cols, const L &l) const { + void operator()(const node_type& cols, const L& l) const { iterate_ast(cols.columns, l); } }; template<class L, class A> - struct ast_iterator<in_t<L, A>, void> { - using node_type = in_t<L, A>; + struct ast_iterator<dynamic_in_t<L, A>, void> { + using node_type = dynamic_in_t<L, A>; + + template<class C> + void operator()(const node_type& in, const C& l) const { + iterate_ast(in.left, l); + iterate_ast(in.argument, l); + } + }; + + template<class L, class... Args> + struct ast_iterator<in_t<L, Args...>, void> { + using node_type = in_t<L, Args...>; template<class C> - void operator()(const node_type &in, const C &l) const { - iterate_ast(in.l, l); - iterate_ast(in.arg, l); + void operator()(const node_type& in, const C& l) const { + iterate_ast(in.left, l); + iterate_ast(in.argument, l); } }; @@ -7686,8 +10789,8 @@ namespace sqlite_orm { using node_type = std::vector<T>; template<class L> - void operator()(const node_type &vec, const L &l) const { - for(auto &i: vec) { + void operator()(const node_type& vec, const L& l) const { + for(auto& i: vec) { iterate_ast(i, l); } } @@ -7698,7 +10801,7 @@ namespace sqlite_orm { using node_type = std::vector<char>; template<class L> - void operator()(const node_type &vec, const L &l) const { + void operator()(const node_type& vec, const L& l) const { l(vec); } }; @@ -7708,18 +10811,48 @@ namespace sqlite_orm { using node_type = T; template<class L> - void operator()(const node_type &c, const L &l) const { + void operator()(const node_type& c, const L& l) const { iterate_ast(c.left, l); iterate_ast(c.right, l); } }; + template<class T> + struct ast_iterator<into_t<T>, void> { + using node_type = into_t<T>; + + template<class L> + void operator()(const node_type& node, const L& l) const { + //.. + } + }; + + template<class... Args> + struct ast_iterator<insert_raw_t<Args...>, void> { + using node_type = insert_raw_t<Args...>; + + template<class L> + void operator()(const node_type& node, const L& l) const { + iterate_ast(node.args, l); + } + }; + + template<class... Args> + struct ast_iterator<replace_raw_t<Args...>, void> { + using node_type = replace_raw_t<Args...>; + + template<class L> + void operator()(const node_type& node, const L& l) const { + iterate_ast(node.args, l); + } + }; + template<class T, class... Args> struct ast_iterator<select_t<T, Args...>, void> { using node_type = select_t<T, Args...>; template<class L> - void operator()(const node_type &sel, const L &l) const { + void operator()(const node_type& sel, const L& l) const { iterate_ast(sel.col, l); iterate_ast(sel.conditions, l); } @@ -7730,7 +10863,7 @@ namespace sqlite_orm { using node_type = get_all_t<T, R, Args...>; template<class L> - void operator()(const node_type &get, const L &l) const { + void operator()(const node_type& get, const L& l) const { iterate_ast(get.conditions, l); } }; @@ -7740,7 +10873,7 @@ namespace sqlite_orm { using node_type = get_all_pointer_t<T, Args...>; template<class L> - void operator()(const node_type &get, const L &l) const { + void operator()(const node_type& get, const L& l) const { iterate_ast(get.conditions, l); } }; @@ -7751,7 +10884,7 @@ namespace sqlite_orm { using node_type = get_all_optional_t<T, Args...>; template<class L> - void operator()(const node_type &get, const L &l) const { + void operator()(const node_type& get, const L& l) const { iterate_ast(get.conditions, l); } }; @@ -7762,7 +10895,7 @@ namespace sqlite_orm { using node_type = update_all_t<set_t<Args...>, Wargs...>; template<class L> - void operator()(const node_type &u, const L &l) const { + void operator()(const node_type& u, const L& l) const { iterate_ast(u.set, l); iterate_ast(u.conditions, l); } @@ -7773,7 +10906,7 @@ namespace sqlite_orm { using node_type = remove_all_t<T, Args...>; template<class L> - void operator()(const node_type &r, const L &l) const { + void operator()(const node_type& r, const L& l) const { iterate_ast(r.conditions, l); } }; @@ -7783,7 +10916,7 @@ namespace sqlite_orm { using node_type = set_t<Args...>; template<class L> - void operator()(const node_type &s, const L &l) const { + void operator()(const node_type& s, const L& l) const { iterate_ast(s.assigns, l); } }; @@ -7793,8 +10926,8 @@ namespace sqlite_orm { using node_type = std::tuple<Args...>; template<class L> - void operator()(const node_type &tuple, const L &l) const { - iterate_tuple(tuple, [&l](auto &v) { + void operator()(const node_type& tuple, const L& l) const { + iterate_tuple(tuple, [&l](auto& v) { iterate_ast(v, l); }); } @@ -7805,7 +10938,7 @@ namespace sqlite_orm { using node_type = having_t<T>; template<class L> - void operator()(const node_type &hav, const L &l) const { + void operator()(const node_type& hav, const L& l) const { iterate_ast(hav.t, l); } }; @@ -7815,7 +10948,7 @@ namespace sqlite_orm { using node_type = cast_t<T, E>; template<class L> - void operator()(const node_type &c, const L &l) const { + void operator()(const node_type& c, const L& l) const { iterate_ast(c.expression, l); } }; @@ -7825,7 +10958,7 @@ namespace sqlite_orm { using node_type = exists_t<T>; template<class L> - void operator()(const node_type &e, const L &l) const { + void operator()(const node_type& e, const L& l) const { iterate_ast(e.t, l); } }; @@ -7835,10 +10968,10 @@ namespace sqlite_orm { using node_type = like_t<A, T, E>; template<class L> - void operator()(const node_type &lk, const L &l) const { + void operator()(const node_type& lk, const L& l) const { iterate_ast(lk.arg, l); iterate_ast(lk.pattern, l); - lk.arg3.apply([&l](auto &value) { + lk.arg3.apply([&l](auto& value) { iterate_ast(value, l); }); } @@ -7849,7 +10982,7 @@ namespace sqlite_orm { using node_type = glob_t<A, T>; template<class L> - void operator()(const node_type &lk, const L &l) const { + void operator()(const node_type& lk, const L& l) const { iterate_ast(lk.arg, l); iterate_ast(lk.pattern, l); } @@ -7860,7 +10993,7 @@ namespace sqlite_orm { using node_type = between_t<A, T>; template<class L> - void operator()(const node_type &b, const L &l) const { + void operator()(const node_type& b, const L& l) const { iterate_ast(b.expr, l); iterate_ast(b.b1, l); iterate_ast(b.b2, l); @@ -7872,7 +11005,7 @@ namespace sqlite_orm { using node_type = named_collate<T>; template<class L> - void operator()(const node_type &col, const L &l) const { + void operator()(const node_type& col, const L& l) const { iterate_ast(col.expr, l); } }; @@ -7882,7 +11015,7 @@ namespace sqlite_orm { using node_type = negated_condition_t<C>; template<class L> - void operator()(const node_type &neg, const L &l) const { + void operator()(const node_type& neg, const L& l) const { iterate_ast(neg.c, l); } }; @@ -7892,7 +11025,7 @@ namespace sqlite_orm { using node_type = is_null_t<T>; template<class L> - void operator()(const node_type &i, const L &l) const { + void operator()(const node_type& i, const L& l) const { iterate_ast(i.t, l); } }; @@ -7902,17 +11035,27 @@ namespace sqlite_orm { using node_type = is_not_null_t<T>; template<class L> - void operator()(const node_type &i, const L &l) const { + void operator()(const node_type& i, const L& l) const { iterate_ast(i.t, l); } }; + template<class F, class... Args> + struct ast_iterator<function_call<F, Args...>, void> { + using node_type = function_call<F, Args...>; + + template<class L> + void operator()(const node_type& f, const L& l) const { + iterate_ast(f.args, l); + } + }; + template<class R, class S, class... Args> - struct ast_iterator<core_function_t<R, S, Args...>, void> { - using node_type = core_function_t<R, S, Args...>; + struct ast_iterator<built_in_function_t<R, S, Args...>, void> { + using node_type = built_in_function_t<R, S, Args...>; template<class L> - void operator()(const node_type &f, const L &l) const { + void operator()(const node_type& f, const L& l) const { iterate_ast(f.args, l); } }; @@ -7922,7 +11065,7 @@ namespace sqlite_orm { using node_type = left_join_t<T, O>; template<class L> - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -7932,7 +11075,7 @@ namespace sqlite_orm { using node_type = on_t<T>; template<class L> - void operator()(const node_type &o, const L &l) const { + void operator()(const node_type& o, const L& l) const { iterate_ast(o.arg, l); } }; @@ -7942,7 +11085,7 @@ namespace sqlite_orm { using node_type = join_t<T, O>; template<class L> - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -7952,7 +11095,7 @@ namespace sqlite_orm { using node_type = left_outer_join_t<T, O>; template<class L> - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -7962,7 +11105,7 @@ namespace sqlite_orm { using node_type = inner_join_t<T, O>; template<class L> - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -7972,15 +11115,15 @@ namespace sqlite_orm { using node_type = simple_case_t<R, T, E, Args...>; template<class L> - void operator()(const node_type &c, const L &l) const { - c.case_expression.apply([&l](auto &c_) { + void operator()(const node_type& c, const L& l) const { + c.case_expression.apply([&l](auto& c_) { iterate_ast(c_, l); }); - iterate_tuple(c.args, [&l](auto &pair) { + iterate_tuple(c.args, [&l](auto& pair) { iterate_ast(pair.first, l); iterate_ast(pair.second, l); }); - c.else_expression.apply([&l](auto &el) { + c.else_expression.apply([&l](auto& el) { iterate_ast(el, l); }); } @@ -7991,7 +11134,7 @@ namespace sqlite_orm { using node_type = as_t<T, E>; template<class L> - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.expression, l); } }; @@ -8001,7 +11144,7 @@ namespace sqlite_orm { using node_type = limit_t<T, false, OI, void>; template<class L> - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.lim, l); } }; @@ -8011,9 +11154,9 @@ namespace sqlite_orm { using node_type = limit_t<T, true, false, O>; template<class L> - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.lim, l); - a.off.apply([&l](auto &value) { + a.off.apply([&l](auto& value) { iterate_ast(value, l); }); } @@ -8024,8 +11167,8 @@ namespace sqlite_orm { using node_type = limit_t<T, true, true, O>; template<class L> - void operator()(const node_type &a, const L &l) const { - a.off.apply([&l](auto &value) { + void operator()(const node_type& a, const L& l) const { + a.off.apply([&l](auto& value) { iterate_ast(value, l); }); iterate_ast(a.lim, l); @@ -8037,8 +11180,8 @@ namespace sqlite_orm { using node_type = distinct_t<T>; template<class L> - void operator()(const node_type &a, const L &l) const { - iterate_ast(a.t, l); + void operator()(const node_type& a, const L& l) const { + iterate_ast(a.value, l); } }; @@ -8047,8 +11190,8 @@ namespace sqlite_orm { using node_type = all_t<T>; template<class L> - void operator()(const node_type &a, const L &l) const { - iterate_ast(a.t, l); + void operator()(const node_type& a, const L& l) const { + iterate_ast(a.value, l); } }; @@ -8057,7 +11200,7 @@ namespace sqlite_orm { using node_type = bitwise_not_t<T>; template<class L> - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.argument, l); } }; @@ -8067,7 +11210,7 @@ namespace sqlite_orm { using node_type = values_t<Args...>; template<class L> - void operator()(const node_type &node, const L &l) const { + void operator()(const node_type& node, const L& l) const { iterate_ast(node.tuple, l); } }; @@ -8077,7 +11220,7 @@ namespace sqlite_orm { using node_type = dynamic_values_t<T>; template<class L> - void operator()(const node_type &node, const L &l) const { + void operator()(const node_type& node, const L& l) const { iterate_ast(node.vector, l); } }; @@ -8087,7 +11230,7 @@ namespace sqlite_orm { using node_type = collate_t<T>; template<class L> - void operator()(const node_type &node, const L &l) const { + void operator()(const node_type& node, const L& l) const { iterate_ast(node.expr, l); } }; @@ -8103,17 +11246,26 @@ namespace sqlite_orm { namespace internal { + /** + * This class does not related to SQL view. This is a container like class which is returned by + * by storage_t::iterate function. This class contains STL functions: + * - size_t size() + * - bool empty() + * - iterator end() + * - iterator begin() + * All these functions are not right const cause all of them may open SQLite connections. + */ template<class T, class S, class... Args> struct view_t { using mapped_type = T; using storage_type = S; using self = view_t<T, S, Args...>; - storage_type &storage; + storage_type& storage; connection_ref connection; get_all_t<T, std::vector<T>, Args...> args; - view_t(storage_type &stor, decltype(connection) conn, Args &&... args_) : + view_t(storage_type& stor, decltype(connection) conn, Args&&... args_) : storage(stor), connection(std::move(conn)), args{std::make_tuple(std::forward<Args>(args_)...)} {} size_t size() { @@ -8124,12 +11276,8 @@ namespace sqlite_orm { return !this->size(); } - iterator_t<self> end() { - return {nullptr, *this}; - } - iterator_t<self> begin() { - sqlite3_stmt *stmt = nullptr; + sqlite3_stmt* stmt = nullptr; auto db = this->connection.get(); using context_t = serializator_context<typename storage_type::impl_type>; context_t context{this->storage.impl}; @@ -8139,7 +11287,7 @@ namespace sqlite_orm { auto ret = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); if(ret == SQLITE_OK) { auto index = 1; - iterate_ast(this->args.conditions, [&index, stmt, db](auto &node) { + iterate_ast(this->args.conditions, [&index, stmt, db](auto& node) { using node_type = typename std::decay<decltype(node)>::type; conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -8153,6 +11301,10 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } } + + iterator_t<self> end() { + return {}; + } }; } } @@ -8179,9 +11331,12 @@ namespace sqlite_orm { #include <sqlite3.h> #include <functional> // std::function #include <memory> // std::shared_ptr +#include <vector> // std::vector // #include "error_code.h" +// #include "util.h" + // #include "row_extractor.h" // #include "journal_mode.h" @@ -8192,126 +11347,141 @@ namespace sqlite_orm { namespace internal { struct storage_base; - } - - struct pragma_t { - using get_connection_t = std::function<internal::connection_ref()>; - pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} - - void busy_timeout(int value) { - this->set_pragma("busy_timeout", value); + template<class T> + int getPragmaCallback(void* data, int argc, char** argv, char**) { + auto& res = *(T*)data; + if(argc) { + res = row_extractor<T>().extract(argv[0]); + } + return 0; } - int busy_timeout() { - return this->get_pragma<int>("busy_timeout"); + template<> + inline int getPragmaCallback<std::vector<std::string>>(void* data, int argc, char** argv, char**) { + auto& res = *(std::vector<std::string>*)data; + res.reserve(argc); + for(decltype(argc) i = 0; i < argc; ++i) { + auto rowString = row_extractor<std::string>().extract(argv[i]); + res.push_back(move(rowString)); + } + return 0; } - sqlite_orm::journal_mode journal_mode() { - return this->get_pragma<sqlite_orm::journal_mode>("journal_mode"); - } + struct pragma_t { + using get_connection_t = std::function<internal::connection_ref()>; - void journal_mode(sqlite_orm::journal_mode value) { - this->_journal_mode = -1; - this->set_pragma("journal_mode", value); - this->_journal_mode = static_cast<decltype(this->_journal_mode)>(value); - } + pragma_t(get_connection_t get_connection_) : get_connection(move(get_connection_)) {} - int synchronous() { - return this->get_pragma<int>("synchronous"); - } + void busy_timeout(int value) { + this->set_pragma("busy_timeout", value); + } - void synchronous(int value) { - this->_synchronous = -1; - this->set_pragma("synchronous", value); - this->_synchronous = value; - } + int busy_timeout() { + return this->get_pragma<int>("busy_timeout"); + } - int user_version() { - return this->get_pragma<int>("user_version"); - } + sqlite_orm::journal_mode journal_mode() { + return this->get_pragma<sqlite_orm::journal_mode>("journal_mode"); + } - void user_version(int value) { - this->set_pragma("user_version", value); - } + void journal_mode(sqlite_orm::journal_mode value) { + this->_journal_mode = -1; + this->set_pragma("journal_mode", value); + this->_journal_mode = static_cast<decltype(this->_journal_mode)>(value); + } - int auto_vacuum() { - return this->get_pragma<int>("auto_vacuum"); - } + int synchronous() { + return this->get_pragma<int>("synchronous"); + } - void auto_vacuum(int value) { - this->set_pragma("auto_vacuum", value); - } + void synchronous(int value) { + this->_synchronous = -1; + this->set_pragma("synchronous", value); + this->_synchronous = value; + } - protected: - friend struct storage_base; + int user_version() { + return this->get_pragma<int>("user_version"); + } - public: - int _synchronous = -1; - signed char _journal_mode = -1; // if != -1 stores static_cast<sqlite_orm::journal_mode>(journal_mode) - get_connection_t get_connection; - - template<class T> - T get_pragma(const std::string &name) { - auto connection = this->get_connection(); - auto query = "PRAGMA " + name; - T result; - auto db = connection.get(); - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(T *)data; - if(argc) { - res = row_extractor<T>().extract(argv[0]); - } - return 0; - }, - &result, - nullptr); - if(rc == SQLITE_OK) { - return result; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + void user_version(int value) { + this->set_pragma("user_version", value); } - } - /** - * Yevgeniy Zakharov: I wanted to refactore this function with statements and value bindings - * but it turns out that bindings in pragma statements are not supported. - */ - template<class T> - void set_pragma(const std::string &name, const T &value, sqlite3 *db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); + int auto_vacuum() { + return this->get_pragma<int>("auto_vacuum"); } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << value; - auto query = ss.str(); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + + void auto_vacuum(int value) { + this->set_pragma("auto_vacuum", value); } - } - void set_pragma(const std::string &name, const sqlite_orm::journal_mode &value, sqlite3 *db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); + std::vector<std::string> integrity_check() { + return this->get_pragma<std::vector<std::string>>("integrity_check"); } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << internal::to_string(value); - auto query = ss.str(); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + + template<class T> + std::vector<std::string> integrity_check(T table_name) { + std::ostringstream oss; + oss << "integrity_check(" << table_name << ")"; + return this->get_pragma<std::vector<std::string>>(oss.str()); } - } - }; + + std::vector<std::string> integrity_check(int n) { + std::ostringstream oss; + oss << "integrity_check(" << n << ")"; + return this->get_pragma<std::vector<std::string>>(oss.str()); + } + + private: + friend struct storage_base; + + int _synchronous = -1; + signed char _journal_mode = -1; // if != -1 stores static_cast<sqlite_orm::journal_mode>(journal_mode) + get_connection_t get_connection; + + template<class T> + T get_pragma(const std::string& name) { + auto connection = this->get_connection(); + auto query = "PRAGMA " + name; + T result; + auto db = connection.get(); + auto rc = sqlite3_exec(db, query.c_str(), getPragmaCallback<T>, &result, nullptr); + if(rc == SQLITE_OK) { + return result; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + /** + * Yevgeniy Zakharov: I wanted to refactor this function with statements and value bindings + * but it turns out that bindings in pragma statements are not supported. + */ + template<class T> + void set_pragma(const std::string& name, const T& value, sqlite3* db = nullptr) { + auto con = this->get_connection(); + if(!db) { + db = con.get(); + } + std::stringstream ss; + ss << "PRAGMA " << name << " = " << value; + internal::perform_void_exec(db, ss.str()); + } + + void set_pragma(const std::string& name, const sqlite_orm::journal_mode& value, sqlite3* db = nullptr) { + auto con = this->get_connection(); + if(!db) { + db = con.get(); + } + std::stringstream ss; + ss << "PRAGMA " << name << " = " << internal::to_string(value); + internal::perform_void_exec(db, ss.str()); + } + }; + } } // #include "limit_accesor.h" @@ -8526,10 +11696,12 @@ namespace sqlite_orm { // #include "type_printer.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "row_extractor.h" +// #include "util.h" + // #include "connection_holder.h" // #include "backup.h" @@ -8554,9 +11726,9 @@ namespace sqlite_orm { */ struct backup_t { backup_t(connection_ref to_, - const std::string &zDestName, + const std::string& zDestName, connection_ref from_, - const std::string &zSourceName, + const std::string& zSourceName, std::unique_ptr<connection_holder> holder_) : handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), to(to_), from(from_), holder(move(holder_)) { @@ -8565,7 +11737,7 @@ namespace sqlite_orm { } } - backup_t(backup_t &&other) : + backup_t(backup_t&& other) : handle(other.handle), to(other.to), from(other.from), holder(move(other.holder)) { other.handle = nullptr; } @@ -8599,7 +11771,7 @@ namespace sqlite_orm { } protected: - sqlite3_backup *handle = nullptr; + sqlite3_backup* handle = nullptr; connection_ref to; connection_ref from; std::unique_ptr<connection_holder> holder; @@ -8607,14 +11779,206 @@ namespace sqlite_orm { } } +// #include "function.h" + +// #include "values_to_tuple.h" + +#include <sqlite3.h> +#include <tuple> // std::get, std::tuple_element + +// #include "row_extractor.h" + +// #include "arg_values.h" + +#include <sqlite3.h> + +// #include "row_extractor.h" + +namespace sqlite_orm { + + struct arg_value { + + arg_value() : arg_value(nullptr) {} + + arg_value(sqlite3_value* value_) : value(value_) {} + + template<class T> + T get() const { + return row_extractor<T>().extract(this->value); + } + + bool is_null() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_NULL; + } + + bool is_text() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_TEXT; + } + + bool is_integer() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_INTEGER; + } + + bool is_float() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_FLOAT; + } + + bool is_blob() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_BLOB; + } + + bool empty() const { + return this->value == nullptr; + } + + private: + sqlite3_value* value = nullptr; + }; + + struct arg_values { + + struct iterator { + + iterator(const arg_values& container_, int index_) : + container(container_), index(index_), + currentValue(index_ < int(container_.size()) ? container_[index_] : arg_value()) {} + + iterator& operator++() { + ++this->index; + if(this->index < int(this->container.size())) { + this->currentValue = this->container[this->index]; + } else { + this->currentValue = {}; + } + return *this; + } + + iterator operator++(int) { + auto res = *this; + ++this->index; + if(this->index < int(this->container.size())) { + this->currentValue = this->container[this->index]; + } else { + this->currentValue = {}; + } + return res; + } + + arg_value operator*() const { + if(this->index < int(this->container.size()) && this->index >= 0) { + return this->currentValue; + } else { + throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + } + } + + arg_value* operator->() const { + return &this->currentValue; + } + + bool operator==(const iterator& other) const { + return &other.container == &this->container && other.index == this->index; + } + + bool operator!=(const iterator& other) const { + return !(*this == other); + } + + private: + const arg_values& container; + int index = 0; + mutable arg_value currentValue; + }; + + arg_values() : arg_values(0, nullptr) {} + + arg_values(int argsCount_, sqlite3_value** values_) : argsCount(argsCount_), values(values_) {} + + size_t size() const { + return this->argsCount; + } + + bool empty() const { + return 0 == this->argsCount; + } + + arg_value operator[](int index) const { + if(index < this->argsCount && index >= 0) { + auto valuePointer = this->values[index]; + return {valuePointer}; + } else { + throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + } + } + + arg_value at(int index) const { + return this->operator[](index); + } + + iterator begin() const { + return {*this, 0}; + } + + iterator end() const { + return {*this, this->argsCount}; + } + + private: + int argsCount = 0; + sqlite3_value** values = nullptr; + }; +} + +namespace sqlite_orm { + + namespace internal { + + /** + * T is a std::tuple type + * I is index to extract value from values C array to tuple. I must me < std::tuple_size<T>::value + */ + template<class T, int I> + struct values_to_tuple { + + void extract(sqlite3_value** values, T& tuple, int argsCount) const { + using element_type = typename std::tuple_element<I, T>::type; + std::get<I>(tuple) = row_extractor<element_type>().extract(values[I]); + + values_to_tuple<T, I - 1>().extract(values, tuple, argsCount); + } + }; + + template<class T> + struct values_to_tuple<T, -1> { + void extract(sqlite3_value** values, T& tuple, int argsCount) const { + //.. + } + }; + + template<> + struct values_to_tuple<std::tuple<arg_values>, 0> { + void extract(sqlite3_value** values, std::tuple<arg_values>& tuple, int argsCount) const { + std::get<0>(tuple) = arg_values(argsCount, values); + } + }; + } +} + +// #include "arg_values.h" + namespace sqlite_orm { namespace internal { struct storage_base { - using collating_function = std::function<int(int, const void *, int, const void *)>; + using collating_function = std::function<int(int, const void*, int, const void*)>; - std::function<void(sqlite3 *)> on_open; + std::function<void(sqlite3*)> on_open; pragma_t pragma; limit_accesor limit; @@ -8625,50 +11989,20 @@ namespace sqlite_orm { return {this->get_connection(), move(commitFunc), move(rollbackFunc)}; } - void drop_index(const std::string &indexName) { - auto con = this->get_connection(); - auto db = con.get(); + void drop_index(const std::string& indexName) { std::stringstream ss; ss << "DROP INDEX '" << indexName + "'"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(get_connection().get(), ss.str()); } void vacuum() { - auto con = this->get_connection(); - auto db = con.get(); - std::string query = "VACUUM"; - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(get_connection().get(), "VACUUM"); } /** * Drops table with given name. */ - void drop_table(const std::string &tableName) { + void drop_table(const std::string& tableName) { auto con = this->get_connection(); this->drop_table_internal(tableName, con.get()); } @@ -8676,11 +12010,10 @@ namespace sqlite_orm { /** * Rename table named `from` to `to`. */ - void rename_table(const std::string &from, const std::string &to) { - auto con = this->get_connection(); + void rename_table(const std::string& from, const std::string& to) { std::stringstream ss; ss << "ALTER TABLE '" << from << "' RENAME TO '" << to << "'"; - this->perform_query_without_result(ss.str(), con.get()); + perform_void_exec(get_connection().get(), ss.str()); } /** @@ -8716,13 +12049,13 @@ namespace sqlite_orm { return sqlite3_libversion(); } - bool transaction(const std::function<bool()> &f) { - this->begin_transaction(); + bool transaction(const std::function<bool()>& f) { + auto guard = transaction_guard(); auto shouldCommit = f(); if(shouldCommit) { - this->commit(); + guard.commit(); } else { - this->rollback(); + guard.rollback(); } return shouldCommit; } @@ -8759,8 +12092,8 @@ namespace sqlite_orm { int res = sqlite3_exec( db, sql.c_str(), - [](void *data, int argc, char **argv, char * * /*columnName*/) -> int { - auto &tableNames_ = *(data_t *)data; + [](void* data, int argc, char** argv, char** /*columnName*/) -> int { + auto& tableNames_ = *(data_t*)data; for(int i = 0; i < argc; i++) { if(argv[i]) { tableNames_.push_back(argv[i]); @@ -8786,9 +12119,166 @@ namespace sqlite_orm { } } - void create_collation(const std::string &name, collating_function f) { - collating_function *functionPointer = nullptr; - if(f) { + /** + * Call this to create user defined scalar function. Can be called at any time no matter connection is opened or no. + * T - function class. T must have operator() overload and static name function like this: + * ``` + * struct SqrtFunction { + * + * double operator()(double arg) const { + * return std::sqrt(arg); + * } + * + * static const char *name() { + * return "SQRT"; + * } + * }; + * ``` + */ + template<class F> + void create_scalar_function() { + static_assert(is_scalar_function<F>::value, "F cannot be a scalar function"); + + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + using args_tuple = typename callable_arguments<F>::args_tuple; + using return_type = typename callable_arguments<F>::return_type; + auto argsCount = int(std::tuple_size<args_tuple>::value); + if(std::is_same<args_tuple, std::tuple<arg_values>>::value) { + argsCount = -1; + } + this->scalarFunctions.emplace_back(new scalar_function_t{ + move(name), + argsCount, + []() -> int* { + return (int*)(new F()); + }, + /* call = */ + [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& functionPointer = *static_cast<F*>(functionVoidPointer); + args_tuple argsTuple; + using tuple_size = std::tuple_size<args_tuple>; + values_to_tuple<args_tuple, tuple_size::value - 1>().extract(values, argsTuple, argsCount); + auto result = call(functionPointer, std::move(argsTuple)); + statement_binder<return_type>().result(context, result); + }, + delete_function_callback<F>, + }); + + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + try_to_create_function(db, static_cast<scalar_function_t&>(*this->scalarFunctions.back())); + } + } + + /** + * Call this to create user defined aggregate function. Can be called at any time no matter connection is opened or no. + * T - function class. T must have step member function, fin member function and static name function like this: + * ``` + * struct MeanFunction { + * double total = 0; + * int count = 0; + * + * void step(double value) { + * total += value; + * ++count; + * } + * + * int fin() const { + * return total / count; + * } + * + * static std::string name() { + * return "MEAN"; + * } + * }; + * ``` + */ + template<class F> + void create_aggregate_function() { + static_assert(is_aggregate_function<F>::value, "F cannot be an aggregate function"); + + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + using args_tuple = typename callable_arguments<F>::args_tuple; + using return_type = typename callable_arguments<F>::return_type; + auto argsCount = int(std::tuple_size<args_tuple>::value); + if(std::is_same<args_tuple, std::tuple<arg_values>>::value) { + argsCount = -1; + } + this->aggregateFunctions.emplace_back(new aggregate_function_t{ + move(name), + argsCount, + /* create = */ + []() -> int* { + return (int*)(new F()); + }, + /* step = */ + [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& functionPointer = *static_cast<F*>(functionVoidPointer); + args_tuple argsTuple; + using tuple_size = std::tuple_size<args_tuple>; + values_to_tuple<args_tuple, tuple_size::value - 1>().extract(values, argsTuple, argsCount); + call(functionPointer, &F::step, move(argsTuple)); + }, + /* finalCall = */ + [](sqlite3_context* context, void* functionVoidPointer) { + auto& functionPointer = *static_cast<F*>(functionVoidPointer); + auto result = functionPointer.fin(); + statement_binder<return_type>().result(context, result); + }, + delete_function_callback<F>, + }); + + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + try_to_create_function(db, static_cast<aggregate_function_t&>(*this->aggregateFunctions.back())); + } + } + + /** + * Use it to delete scalar function you created before. Can be called at any time no matter connection is open or no. + */ + template<class F> + void delete_scalar_function() { + static_assert(is_scalar_function<F>::value, "F cannot be a scalar function"); + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + this->delete_function_impl(name, this->scalarFunctions); + } + + /** + * Use it to delete aggregate function you created before. Can be called at any time no matter connection is open or no. + */ + template<class F> + void delete_aggregate_function() { + static_assert(is_aggregate_function<F>::value, "F cannot be an aggregate function"); + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + this->delete_function_impl(name, this->aggregateFunctions); + } + + template<class C> + void create_collation() { + collating_function func = [](int leftLength, const void* lhs, int rightLength, const void* rhs) { + C collatingObject; + return collatingObject(leftLength, lhs, rightLength, rhs); + }; + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + this->create_collation(name, move(func)); + } + + void create_collation(const std::string& name, collating_function f) { + collating_function* functionPointer = nullptr; + const auto functionExists = bool(f); + if(functionExists) { functionPointer = &(collatingFunctions[name] = std::move(f)); } else { collatingFunctions.erase(name); @@ -8797,29 +12287,39 @@ namespace sqlite_orm { // create collations if db is open if(this->connection->retain_count() > 0) { auto db = this->connection->get(); - if(sqlite3_create_collation(db, - name.c_str(), - SQLITE_UTF8, - functionPointer, - f ? collate_callback : nullptr) != SQLITE_OK) { + auto resultCode = sqlite3_create_collation(db, + name.c_str(), + SQLITE_UTF8, + functionPointer, + functionExists ? collate_callback : nullptr); + if(resultCode != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } } + template<class C> + void delete_collation() { + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + this->create_collation(name, {}); + } + void begin_transaction() { this->connection->retain(); if(1 == this->connection->retain_count()) { this->on_open_internal(this->connection->get()); } auto db = this->connection->get(); - this->begin_transaction(db); + perform_void_exec(db, "BEGIN TRANSACTION"); } void commit() { auto db = this->connection->get(); - this->commit(db); + perform_void_exec(db, "COMMIT"); this->connection->release(); if(this->connection->retain_count() < 0) { throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); @@ -8828,54 +12328,54 @@ namespace sqlite_orm { void rollback() { auto db = this->connection->get(); - this->rollback(db); + perform_void_exec(db, "ROLLBACK"); this->connection->release(); if(this->connection->retain_count() < 0) { throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); } } - void backup_to(const std::string &filename) { + void backup_to(const std::string& filename) { auto backup = this->make_backup_to(filename); backup.step(-1); } - void backup_to(storage_base &other) { + void backup_to(storage_base& other) { auto backup = this->make_backup_to(other); backup.step(-1); } - void backup_from(const std::string &filename) { + void backup_from(const std::string& filename) { auto backup = this->make_backup_from(filename); backup.step(-1); } - void backup_from(storage_base &other) { + void backup_from(storage_base& other) { auto backup = this->make_backup_from(other); backup.step(-1); } - backup_t make_backup_to(const std::string &filename) { + backup_t make_backup_to(const std::string& filename) { auto holder = std::make_unique<connection_holder>(filename); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", move(holder)}; } - backup_t make_backup_to(storage_base &other) { + backup_t make_backup_to(storage_base& other) { return {other.get_connection(), "main", this->get_connection(), "main", {}}; } - backup_t make_backup_from(const std::string &filename) { + backup_t make_backup_from(const std::string& filename) { auto holder = std::make_unique<connection_holder>(filename); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", move(holder)}; } - backup_t make_backup_from(storage_base &other) { + backup_t make_backup_from(storage_base& other) { return {this->get_connection(), "main", other.get_connection(), "main", {}}; } - const std::string &filename() const { + const std::string& filename() const { return this->connection->filename; } @@ -8901,7 +12401,7 @@ namespace sqlite_orm { } protected: - storage_base(const std::string &filename_, int foreignKeysCount) : + storage_base(const std::string& filename_, int foreignKeysCount) : pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(filename_.empty() || filename_ == ":memory:"), @@ -8912,7 +12412,7 @@ namespace sqlite_orm { } } - storage_base(const storage_base &other) : + storage_base(const storage_base& other) : on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), connection(std::make_unique<connection_holder>(other.connection->filename)), @@ -8932,13 +12432,6 @@ namespace sqlite_orm { } } - const bool inMemory; - bool isOpenedForever = false; - std::unique_ptr<connection_holder> connection; - std::map<std::string, collating_function> collatingFunctions; - const int cachedForeignKeysCount; - std::function<int(int)> _busy_handler; - public: connection_ref get_connection() { connection_ref res{*this->connection}; @@ -8948,28 +12441,22 @@ namespace sqlite_orm { return res; } - protected: #if SQLITE_VERSION_NUMBER >= 3006019 - void foreign_keys(sqlite3 *db, bool value) { + void foreign_keys(sqlite3* db, bool value) { std::stringstream ss; ss << "PRAGMA foreign_keys = " << value; - auto query = ss.str(); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(db, ss.str()); } - bool foreign_keys(sqlite3 *db) { + bool foreign_keys(sqlite3* db) { std::string query = "PRAGMA foreign_keys"; auto result = false; auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(bool *)data; + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(bool*)data; if(argc) { res = row_extractor<bool>().extract(argv[0]); } @@ -8985,7 +12472,7 @@ namespace sqlite_orm { } #endif - void on_open_internal(sqlite3 *db) { + void on_open_internal(sqlite3* db) { #if SQLITE_VERSION_NUMBER >= 3006019 if(this->cachedForeignKeysCount) { @@ -9000,15 +12487,16 @@ namespace sqlite_orm { this->pragma.set_pragma("journal_mode", static_cast<journal_mode>(this->pragma._journal_mode), db); } - for(auto &p: this->collatingFunctions) { - if(sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback) != - SQLITE_OK) { + for(auto& p: this->collatingFunctions) { + auto resultCode = + sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); + if(resultCode != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } - for(auto &p: this->limit.limits) { + for(auto& p: this->limit.limits) { sqlite3_limit(db, p.first, p.second); } @@ -9016,69 +12504,120 @@ namespace sqlite_orm { sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); } + for(auto& functionPointer: this->scalarFunctions) { + try_to_create_function(db, static_cast<scalar_function_t&>(*functionPointer)); + } + + for(auto& functionPointer: this->aggregateFunctions) { + try_to_create_function(db, static_cast<aggregate_function_t&>(*functionPointer)); + } + if(this->on_open) { this->on_open(db); } } - void begin_transaction(sqlite3 *db) { - std::stringstream ss; - ss << "BEGIN TRANSACTION"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + void delete_function_impl(const std::string& name, + std::vector<std::unique_ptr<function_base>>& functionsVector) const { + auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& functionPointer) { + return functionPointer->name == name; + }); + if(it != functionsVector.end()) { + functionsVector.erase(it); + it = functionsVector.end(); + + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + auto resultCode = sqlite3_create_function_v2(db, + name.c_str(), + 0, + SQLITE_UTF8, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + if(resultCode != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } } } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw std::system_error(std::make_error_code(orm_error_code::function_not_found)); } } - void commit(sqlite3 *db) { - std::stringstream ss; - ss << "COMMIT"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { + void try_to_create_function(sqlite3* db, scalar_function_t& function) { + auto resultCode = sqlite3_create_function_v2(db, + function.name.c_str(), + function.argumentsCount, + SQLITE_UTF8, + &function, + scalar_function_callback, + nullptr, + nullptr, + nullptr); + if(resultCode != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } - void rollback(sqlite3 *db) { - std::stringstream ss; - ss << "ROLLBACK"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + void try_to_create_function(sqlite3* db, aggregate_function_t& function) { + auto resultCode = sqlite3_create_function(db, + function.name.c_str(), + function.argumentsCount, + SQLITE_UTF8, + &function, + nullptr, + aggregate_function_step_callback, + aggregate_function_final_callback); + if(resultCode != SQLITE_OK) { + throw std::system_error(std::error_code(resultCode, get_sqlite_error_category()), + sqlite3_errstr(resultCode)); + } + } + + static void + aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { + auto functionVoidPointer = sqlite3_user_data(context); + auto functionPointer = static_cast<aggregate_function_t*>(functionVoidPointer); + auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntPointer = static_cast<int**>(aggregateContextVoidPointer); + if(*aggregateContextIntPointer == nullptr) { + *aggregateContextIntPointer = functionPointer->create(); + } + functionPointer->step(context, *aggregateContextIntPointer, argsCount, values); + } + + static void aggregate_function_final_callback(sqlite3_context* context) { + auto functionVoidPointer = sqlite3_user_data(context); + auto functionPointer = static_cast<aggregate_function_t*>(functionVoidPointer); + auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntPointer = static_cast<int**>(aggregateContextVoidPointer); + functionPointer->finalCall(context, *aggregateContextIntPointer); + functionPointer->destroy(*aggregateContextIntPointer); + } + + static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { + auto functionVoidPointer = sqlite3_user_data(context); + auto functionPointer = static_cast<scalar_function_t*>(functionVoidPointer); + std::unique_ptr<int, void (*)(int*)> callablePointer(functionPointer->create(), + functionPointer->destroy); + if(functionPointer->argumentsCount != -1 && functionPointer->argumentsCount != argsCount) { + throw std::system_error(std::make_error_code(orm_error_code::arguments_count_does_not_match)); } + functionPointer->run(context, functionPointer, argsCount, values); } - std::string current_timestamp(sqlite3 *db) { + template<class F> + static void delete_function_callback(int* pointer) { + auto voidPointer = static_cast<void*>(pointer); + auto fPointer = static_cast<F*>(voidPointer); + delete fPointer; + } + + std::string current_timestamp(sqlite3* db) { std::string result; std::stringstream ss; ss << "SELECT CURRENT_TIMESTAMP"; @@ -9086,8 +12625,8 @@ namespace sqlite_orm { auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(std::string *)data; + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::string*)data; if(argc) { if(argv[0]) { res = row_extractor<std::string>().extract(argv[0]); @@ -9104,35 +12643,19 @@ namespace sqlite_orm { return result; } - void drop_table_internal(const std::string &tableName, sqlite3 *db) { + void drop_table_internal(const std::string& tableName, sqlite3* db) { std::stringstream ss; ss << "DROP TABLE '" << tableName + "'"; - this->perform_query_without_result(ss.str(), db); + perform_void_exec(db, ss.str()); } - void perform_query_without_result(const std::string &query, sqlite3 *db) { - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - - static int collate_callback(void *arg, int leftLen, const void *lhs, int rightLen, const void *rhs) { - auto &f = *(collating_function *)arg; + static int collate_callback(void* arg, int leftLen, const void* lhs, int rightLen, const void* rhs) { + auto& f = *(collating_function*)arg; return f(leftLen, lhs, rightLen, rhs); } - static int busy_handler_callback(void *selfPointer, int triesCount) { - auto &storage = *static_cast<storage_base *>(selfPointer); + static int busy_handler_callback(void* selfPointer, int triesCount) { + auto& storage = *static_cast<storage_base*>(selfPointer); if(storage._busy_handler) { return storage._busy_handler(triesCount); } else { @@ -9142,13 +12665,22 @@ namespace sqlite_orm { // returns foreign keys count in storage definition template<class T> - static int foreign_keys_count(T &storageImpl) { + static int foreign_keys_count(T& storageImpl) { auto res = 0; - storageImpl.for_each([&res](auto &impl) { + storageImpl.for_each([&res](auto& impl) { res += impl.foreign_keys_count(); }); return res; } + + const bool inMemory; + bool isOpenedForever = false; + std::unique_ptr<connection_holder> connection; + std::map<std::string, collating_function> collatingFunctions; + const int cachedForeignKeysCount; + std::function<int(int)> _busy_handler; + std::vector<std::unique_ptr<function_base>> scalarFunctions; + std::vector<std::unique_ptr<function_base>> aggregateFunctions; }; } } @@ -9189,6 +12721,16 @@ namespace sqlite_orm { using type = typename std::decay<T>::type; }; + template<class It, class L, class O> + struct expression_object_type<replace_range_t<It, L, O>> { + using type = typename replace_range_t<It, L, O>::object_type; + }; + + template<class It, class L, class O> + struct expression_object_type<replace_range_t<std::reference_wrapper<It>, L, O>> { + using type = typename replace_range_t<std::reference_wrapper<It>, L, O>::object_type; + }; + template<class T> struct expression_object_type<insert_t<T>> { using type = typename std::decay<T>::type; @@ -9199,6 +12741,17 @@ namespace sqlite_orm { using type = typename std::decay<T>::type; }; + template<class It, class L, class O> + struct expression_object_type<insert_range_t<It, L, O>> { + using transformer_type = L; + using type = typename insert_range_t<It, L, O>::object_type; + }; + + template<class It, class L, class O> + struct expression_object_type<insert_range_t<std::reference_wrapper<It>, L, O>> { + using type = typename insert_range_t<std::reference_wrapper<It>, L, O>::object_type; + }; + template<class T, class... Cols> struct expression_object_type<insert_explicit<T, Cols...>> { using type = typename std::decay<T>::type; @@ -9213,7 +12766,7 @@ namespace sqlite_orm { struct get_ref_t { template<class O> - auto &operator()(O &t) const { + auto& operator()(O& t) const { return t; } }; @@ -9222,13 +12775,13 @@ namespace sqlite_orm { struct get_ref_t<std::reference_wrapper<T>> { template<class O> - auto &operator()(O &t) const { + auto& operator()(O& t) const { return t.get(); } }; template<class T> - auto &get_ref(T &t) { + auto& get_ref(T& t) { using arg_type = typename std::decay<T>::type; get_ref_t<arg_type> g; return g(t); @@ -9241,7 +12794,7 @@ namespace sqlite_orm { struct get_object_t<const T> : get_object_t<T> {}; template<class T> - auto &get_object(T &t) { + auto& get_object(T& t) { using expression_type = typename std::decay<T>::type; get_object_t<expression_type> obj; return obj(t); @@ -9252,7 +12805,7 @@ namespace sqlite_orm { using expression_type = replace_t<T>; template<class O> - auto &operator()(O &e) const { + auto& operator()(O& e) const { return get_ref(e.obj); } }; @@ -9262,7 +12815,7 @@ namespace sqlite_orm { using expression_type = insert_t<T>; template<class O> - auto &operator()(O &e) const { + auto& operator()(O& e) const { return get_ref(e.obj); } }; @@ -9272,7 +12825,7 @@ namespace sqlite_orm { using expression_type = update_t<T>; template<class O> - auto &operator()(O &e) const { + auto& operator()(O& e) const { return get_ref(e.obj); } }; @@ -9283,9 +12836,12 @@ namespace sqlite_orm { #include <sstream> // std::stringstream #include <string> // std::string -#include <type_traits> // std::enable_if +#include <type_traits> // std::enable_if, std::remove_pointer #include <vector> // std::vector #include <algorithm> // std::iter_swap +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include <optional> +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED // #include "core_functions.h" @@ -9323,8 +12879,12 @@ namespace sqlite_orm { find_table_name_t find_table_name; mutable table_name_set table_names; + table_name_collector() = default; + + table_name_collector(find_table_name_t _find_table_name) : find_table_name(std::move(_find_table_name)) {} + template<class T> - table_name_set operator()(const T &) const { + table_name_set operator()(const T&) const { return {}; } @@ -9336,19 +12896,19 @@ namespace sqlite_orm { } template<class T, class F> - void operator()(const column_pointer<T, F> &) const { + void operator()(const column_pointer<T, F>&) const { if(this->find_table_name) { table_names.insert({this->find_table_name(typeid(T)), ""}); } } template<class T, class C> - void operator()(const alias_column_t<T, C> &a) const { + void operator()(const alias_column_t<T, C>& a) const { (*this)(a.column, alias_extractor<T>::get()); } template<class T> - void operator()(const count_asterisk_t<T> &) const { + void operator()(const count_asterisk_t<T>&) const { if(this->find_table_name) { auto tableName = this->find_table_name(typeid(T)); if(!tableName.empty()) { @@ -9358,7 +12918,31 @@ namespace sqlite_orm { } template<class T> - void operator()(const asterisk_t<T> &) const { + void operator()(const asterisk_t<T>&) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template<class T> + void operator()(const object_t<T>&) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template<class T> + void operator()(const table_rowid_t<T>&) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template<class T> + void operator()(const table_oid_t<T>&) const { if(this->find_table_name) { auto tableName = this->find_table_name(typeid(T)); table_names.insert(std::make_pair(move(tableName), "")); @@ -9366,7 +12950,7 @@ namespace sqlite_orm { } template<class T> - void operator()(const object_t<T> &) const { + void operator()(const table__rowid_t<T>&) const { if(this->find_table_name) { auto tableName = this->find_table_name(typeid(T)); table_names.insert(std::make_pair(move(tableName), "")); @@ -9393,14 +12977,14 @@ namespace sqlite_orm { namespace internal { template<class T, class C> - std::string serialize(const T &t, const C &context); + std::string serialize(const T& t, const C& context); template<class T, class SFINAE = void> struct column_names_getter { using expression_type = T; template<class C> - std::vector<std::string> operator()(const expression_type &t, const C &context) { + std::vector<std::string> operator()(const expression_type& t, const C& context) { auto newContext = context; newContext.skip_table_name = false; auto columnName = serialize(t, newContext); @@ -9413,7 +12997,7 @@ namespace sqlite_orm { }; template<class T, class C> - std::vector<std::string> get_column_names(const T &t, const C &context) { + std::vector<std::string> get_column_names(const T& t, const C& context) { column_names_getter<T> serializator; return serializator(t, context); } @@ -9423,7 +13007,7 @@ namespace sqlite_orm { using expression_type = std::reference_wrapper<T>; template<class C> - std::vector<std::string> operator()(const expression_type &expression, const C &context) { + std::vector<std::string> operator()(const expression_type& expression, const C& context) { return get_column_names(expression.get(), context); } }; @@ -9433,7 +13017,7 @@ namespace sqlite_orm { using expression_type = asterisk_t<T>; template<class C> - std::vector<std::string> operator()(const expression_type &, const C &) { + std::vector<std::string> operator()(const expression_type&, const C&) { std::vector<std::string> res; res.push_back("*"); return res; @@ -9445,7 +13029,7 @@ namespace sqlite_orm { using expression_type = object_t<T>; template<class C> - std::vector<std::string> operator()(const expression_type &, const C &) { + std::vector<std::string> operator()(const expression_type&, const C&) { std::vector<std::string> res; res.push_back("*"); return res; @@ -9457,12 +13041,12 @@ namespace sqlite_orm { using expression_type = columns_t<Args...>; template<class C> - std::vector<std::string> operator()(const expression_type &cols, const C &context) { + std::vector<std::string> operator()(const expression_type& cols, const C& context) { std::vector<std::string> columnNames; columnNames.reserve(static_cast<size_t>(cols.count)); auto newContext = context; newContext.skip_table_name = false; - iterate_tuple(cols.columns, [&columnNames, &newContext](auto &m) { + iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { auto columnName = serialize(m, newContext); if(columnName.length()) { columnNames.push_back(columnName); @@ -9491,7 +13075,7 @@ namespace sqlite_orm { struct order_by_serializator; template<class T, class C> - std::string serialize_order_by(const T &t, const C &context) { + std::string serialize_order_by(const T& t, const C& context) { order_by_serializator<T> serializator; return serializator(t, context); } @@ -9501,11 +13085,11 @@ namespace sqlite_orm { using statement_type = order_by_t<O>; template<class C> - std::string operator()(const statement_type &orderBy, const C &context) const { + std::string operator()(const statement_type& orderBy, const C& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; - auto columnName = serialize(orderBy.o, newContext); + auto columnName = serialize(orderBy.expression, newContext); ss << columnName << " "; if(orderBy._collate_argument.length()) { ss << "COLLATE " << orderBy._collate_argument << " "; @@ -9527,9 +13111,9 @@ namespace sqlite_orm { using statement_type = dynamic_order_by_t<S>; template<class C> - std::string operator()(const statement_type &orderBy, const C &) const { + std::string operator()(const statement_type& orderBy, const C&) const { std::vector<std::string> expressions; - for(auto &entry: orderBy) { + for(auto& entry: orderBy) { std::string entryString; { std::stringstream ss; @@ -9571,6 +13155,12 @@ namespace sqlite_orm { // #include "indexed_column.h" +// #include "function.h" + +// #include "ast/upsert_clause.h" + +// #include "ast/excluded.h" + namespace sqlite_orm { namespace internal { @@ -9579,7 +13169,7 @@ namespace sqlite_orm { struct statement_serializator; template<class T, class C> - std::string serialize(const T &t, const C &context) { + std::string serialize(const T& t, const C& context) { statement_serializator<T> serializator; return serializator(t, context); } @@ -9589,7 +13179,7 @@ namespace sqlite_orm { using statement_type = T; template<class C> - std::string operator()(const statement_type &statement, const C &context) { + std::string operator()(const statement_type& statement, const C& context) { if(context.replace_bindable_with_question) { return "?"; } else { @@ -9599,11 +13189,38 @@ namespace sqlite_orm { }; template<class T> + struct statement_serializator<excluded_t<T>, void> { + using statement_type = excluded_t<T>; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) { + std::stringstream ss; + ss << "excluded."; + if(auto columnNamePointer = context.impl.column_name(statement.expression)) { + ss << "\"" << *columnNamePointer << "\""; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + return ss.str(); + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<class T> + struct statement_serializator<as_optional_t<T>, void> { + using statement_type = as_optional_t<T>; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) { + return serialize(statement.value, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template<class T> struct statement_serializator<std::reference_wrapper<T>, void> { using statement_type = std::reference_wrapper<T>; template<class C> - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { return serialize(s.get(), context); } }; @@ -9613,33 +13230,78 @@ namespace sqlite_orm { using statement_type = std::nullptr_t; template<class C> - std::string operator()(const statement_type &, const C &) { + std::string operator()(const statement_type&, const C&) { return "?"; } }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct statement_serializator<std::nullopt_t, void> { + using statement_type = std::nullopt_t; + template<class C> + std::string operator()(const statement_type&, const C&) { + return "?"; + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> struct statement_serializator<alias_holder<T>, void> { using statement_type = alias_holder<T>; template<class C> - std::string operator()(const statement_type &, const C &) { + std::string operator()(const statement_type&, const C&) { return T::get(); } }; + template<class... TargetArgs, class... ActionsArgs> + struct statement_serializator<upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>, void> { + using statement_type = upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + ss << "ON CONFLICT"; + iterate_tuple(statement.target_args, [&ss, &context](auto& value) { + using value_type = typename std::decay<decltype(value)>::type; + auto needParenthesis = std::is_member_pointer<value_type>::value; + ss << ' '; + if(needParenthesis) { + ss << '('; + } + ss << serialize(value, context); + if(needParenthesis) { + ss << ')'; + } + }); + ss << ' ' << "DO"; + if(std::tuple_size<typename statement_type::actions_tuple>::value == 0) { + ss << " NOTHING"; + } else { + ss << " UPDATE"; + auto updateContext = context; + updateContext.use_parentheses = false; + iterate_tuple(statement.actions, [&ss, &updateContext](auto& value) { + ss << ' ' << serialize(value, updateContext); + }); + } + return ss.str(); + } + }; + template<class R, class S, class... Args> - struct statement_serializator<core_function_t<R, S, Args...>, void> { - using statement_type = core_function_t<R, S, Args...>; + struct statement_serializator<built_in_function_t<R, S, Args...>, void> { + using statement_type = built_in_function_t<R, S, Args...>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; - ss << static_cast<std::string>(c) << "("; + ss << statement.serialize() << "("; std::vector<std::string> args; - using args_type = typename std::decay<decltype(c)>::type::args_type; + using args_type = typename std::decay<decltype(statement)>::type::args_type; args.reserve(std::tuple_size<args_type>::value); - iterate_tuple(c.args, [&args, &context](auto &v) { + iterate_tuple(statement.args, [&args, &context](auto& v) { args.push_back(serialize(v, context)); }); for(size_t i = 0; i < args.size(); ++i) { @@ -9653,12 +13315,36 @@ namespace sqlite_orm { } }; + template<class F, class... Args> + struct statement_serializator<function_call<F, Args...>, void> { + using statement_type = function_call<F, Args...>; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { + using args_tuple = std::tuple<Args...>; + + std::stringstream ss; + ss << F::name() << "("; + auto index = 0; + iterate_tuple(statement.args, [&context, &ss, &index](auto& v) { + auto value = serialize(v, context); + ss << value; + if(index < std::tuple_size<args_tuple>::value - 1) { + ss << ", "; + } + ++index; + }); + ss << ")"; + return ss.str(); + } + }; + template<class T, class E> struct statement_serializator<as_t<T, E>, void> { using statement_type = as_t<T, E>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto tableAliasString = alias_extractor<T>::get(); return serialize(c.expression, context) + " AS " + tableAliasString; } @@ -9669,7 +13355,7 @@ namespace sqlite_orm { using statement_type = alias_column_t<T, P>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << T::get() << "'."; @@ -9686,21 +13372,21 @@ namespace sqlite_orm { using statement_type = std::string; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { if(context.replace_bindable_with_question) { return "?"; } else { - return "\"" + c + "\""; + return "\'" + c + "\'"; } } }; template<> - struct statement_serializator<const char *, void> { - using statement_type = const char *; + struct statement_serializator<const char*, void> { + using statement_type = const char*; template<class C> - std::string operator()(const char *c, const C &context) const { + std::string operator()(const char* c, const C& context) const { if(context.replace_bindable_with_question) { return "?"; } else { @@ -9714,12 +13400,16 @@ namespace sqlite_orm { using statement_type = F O::*; template<class C> - std::string operator()(const statement_type &m, const C &context) const { + std::string operator()(const statement_type& m, const C& context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "\"" << context.impl.find_table_name(typeid(O)) << "\"."; } - ss << "\"" << context.column_name(m) << "\""; + if(auto columnnamePointer = context.column_name(m)) { + ss << "\"" << *columnnamePointer << "\""; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } return ss.str(); } }; @@ -9729,7 +13419,7 @@ namespace sqlite_orm { using statement_type = rowid_t; template<class C> - std::string operator()(const statement_type &s, const C &) { + std::string operator()(const statement_type& s, const C&) { return static_cast<std::string>(s); } }; @@ -9739,7 +13429,7 @@ namespace sqlite_orm { using statement_type = oid_t; template<class C> - std::string operator()(const statement_type &s, const C &) { + std::string operator()(const statement_type& s, const C&) { return static_cast<std::string>(s); } }; @@ -9749,7 +13439,7 @@ namespace sqlite_orm { using statement_type = _rowid_t; template<class C> - std::string operator()(const statement_type &s, const C &) { + std::string operator()(const statement_type& s, const C&) { return static_cast<std::string>(s); } }; @@ -9759,7 +13449,7 @@ namespace sqlite_orm { using statement_type = table_rowid_t<O>; template<class C> - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; @@ -9774,7 +13464,7 @@ namespace sqlite_orm { using statement_type = table_oid_t<O>; template<class C> - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; @@ -9789,7 +13479,7 @@ namespace sqlite_orm { using statement_type = table__rowid_t<O>; template<class C> - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; @@ -9804,11 +13494,17 @@ namespace sqlite_orm { using statement_type = binary_operator<L, R, Ds...>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto lhs = serialize(c.lhs, context); auto rhs = serialize(c.rhs, context); std::stringstream ss; - ss << "(" << lhs << " " << static_cast<std::string>(c) << " " << rhs << ")"; + if(context.use_parentheses) { + ss << '('; + } + ss << lhs << " " << static_cast<std::string>(c) << " " << rhs; + if(context.use_parentheses) { + ss << ')'; + } return ss.str(); } }; @@ -9818,7 +13514,7 @@ namespace sqlite_orm { using statement_type = count_asterisk_t<T>; template<class C> - std::string operator()(const statement_type &, const C &context) const { + std::string operator()(const statement_type&, const C& context) const { return serialize(count_asterisk_without_type{}, context); } }; @@ -9828,9 +13524,10 @@ namespace sqlite_orm { using statement_type = count_asterisk_without_type; template<class C> - std::string operator()(const statement_type &c, const C &) const { + std::string operator()(const statement_type& c, const C&) const { std::stringstream ss; - ss << static_cast<std::string>(c) << "(*)"; + auto functionName = c.serialize(); + ss << functionName << "(*)"; return ss.str(); } }; @@ -9840,9 +13537,9 @@ namespace sqlite_orm { using statement_type = distinct_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto expr = serialize(c.t, context); + auto expr = serialize(c.value, context); ss << static_cast<std::string>(c) << "(" << expr << ")"; return ss.str(); } @@ -9853,9 +13550,9 @@ namespace sqlite_orm { using statement_type = all_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto expr = serialize(c.t, context); + auto expr = serialize(c.value, context); ss << static_cast<std::string>(c) << "(" << expr << ")"; return ss.str(); } @@ -9866,12 +13563,16 @@ namespace sqlite_orm { using statement_type = column_pointer<T, F>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(T)) << "'."; } - ss << "\"" << context.impl.column_name_simple(c.field) << "\""; + if(auto columnNamePointer = context.impl.column_name_simple(c.field)) { + ss << "\"" << *columnNamePointer << "\""; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } return ss.str(); } }; @@ -9881,7 +13582,7 @@ namespace sqlite_orm { using statement_type = cast_t<T, E>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(c) << " ("; ss << serialize(c.expression, context) << " AS " << type_printer<T>().print() << ")"; @@ -9895,7 +13596,7 @@ namespace sqlite_orm { using statement_type = T; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.left, context) << " "; ss << static_cast<std::string>(c) << " "; @@ -9909,17 +13610,17 @@ namespace sqlite_orm { using statement_type = simple_case_t<R, T, E, Args...>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << "CASE "; - c.case_expression.apply([&ss, context](auto &c_) { + c.case_expression.apply([&ss, context](auto& c_) { ss << serialize(c_, context) << " "; }); - iterate_tuple(c.args, [&ss, context](auto &pair) { + iterate_tuple(c.args, [&ss, context](auto& pair) { ss << "WHEN " << serialize(pair.first, context) << " "; ss << "THEN " << serialize(pair.second, context) << " "; }); - c.else_expression.apply([&ss, context](auto &el) { + c.else_expression.apply([&ss, context](auto& el) { ss << "ELSE " << serialize(el, context) << " "; }); ss << "END"; @@ -9932,7 +13633,7 @@ namespace sqlite_orm { using statement_type = is_null_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast<std::string>(c); return ss.str(); @@ -9944,7 +13645,7 @@ namespace sqlite_orm { using statement_type = is_not_null_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast<std::string>(c); return ss.str(); @@ -9956,7 +13657,7 @@ namespace sqlite_orm { using statement_type = bitwise_not_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(c) << " "; auto cString = serialize(c.argument, context); @@ -9970,7 +13671,7 @@ namespace sqlite_orm { using statement_type = negated_condition_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(c) << " "; auto cString = serialize(c.c, context); @@ -9985,7 +13686,7 @@ namespace sqlite_orm { using statement_type = T; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto leftString = serialize(c.l, context); auto rightString = serialize(c.r, context); std::stringstream ss; @@ -10005,7 +13706,7 @@ namespace sqlite_orm { using statement_type = named_collate<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); @@ -10018,7 +13719,7 @@ namespace sqlite_orm { using statement_type = collate_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); @@ -10027,44 +13728,64 @@ namespace sqlite_orm { }; template<class L, class A> - struct statement_serializator<in_t<L, A>, void> { - using statement_type = in_t<L, A>; + struct statement_serializator<dynamic_in_t<L, A>, void> { + using statement_type = dynamic_in_t<L, A>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto leftString = serialize(c.l, context); + auto leftString = serialize(c.left, context); ss << leftString << " " << static_cast<std::string>(c) << " "; auto newContext = context; newContext.use_parentheses = true; - ss << serialize(c.arg, newContext); + ss << serialize(c.argument, newContext); return ss.str(); } }; template<class L, class E> - struct statement_serializator<in_t<L, std::vector<E>>, void> { - using statement_type = in_t<L, std::vector<E>>; + struct statement_serializator<dynamic_in_t<L, std::vector<E>>, void> { + using statement_type = dynamic_in_t<L, std::vector<E>>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto leftString = serialize(c.l, context); - if(context.use_parentheses) { - ss << '('; - } - ss << leftString << " " << static_cast<std::string>(c) << " ( "; - for(size_t index = 0; index < c.arg.size(); ++index) { - auto &value = c.arg[index]; - ss << " " << serialize(value, context); - if(index < c.arg.size() - 1) { + auto leftString = serialize(c.left, context); + ss << leftString << " " << static_cast<std::string>(c) << " ("; + for(size_t index = 0; index < c.argument.size(); ++index) { + auto& value = c.argument[index]; + ss << serialize(value, context); + if(index < c.argument.size() - 1) { ss << ", "; } } - ss << " )"; - if(context.use_parentheses) { - ss << ')'; + ss << ")"; + return ss.str(); + } + }; + + template<class L, class... Args> + struct statement_serializator<in_t<L, Args...>, void> { + using statement_type = in_t<L, Args...>; + + template<class C> + std::string operator()(const statement_type& c, const C& context) const { + std::stringstream ss; + auto leftString = serialize(c.left, context); + ss << leftString << " " << static_cast<std::string>(c) << " ("; + std::vector<std::string> args; + using args_type = std::tuple<Args...>; + args.reserve(std::tuple_size<args_type>::value); + iterate_tuple(c.argument, [&args, &context](auto& v) { + args.push_back(serialize(v, context)); + }); + for(size_t i = 0; i < args.size(); ++i) { + ss << args[i]; + if(i < args.size() - 1) { + ss << ", "; + } } + ss << ")"; return ss.str(); } }; @@ -10074,12 +13795,12 @@ namespace sqlite_orm { using statement_type = like_t<A, T, E>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast<std::string>(c) << " "; ss << serialize(c.pattern, context); - c.arg3.apply([&ss, &context](auto &value) { + c.arg3.apply([&ss, &context](auto& value) { ss << " ESCAPE " << serialize(value, context); }); return ss.str(); @@ -10091,7 +13812,7 @@ namespace sqlite_orm { using statement_type = glob_t<A, T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast<std::string>(c) << " "; @@ -10105,7 +13826,7 @@ namespace sqlite_orm { using statement_type = between_t<A, T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; auto expr = serialize(c.expr, context); ss << expr << " " << static_cast<std::string>(c) << " "; @@ -10121,7 +13842,7 @@ namespace sqlite_orm { using statement_type = exists_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(c) << " "; ss << serialize(c.t, context); @@ -10130,33 +13851,37 @@ namespace sqlite_orm { }; template<> - struct statement_serializator<constraints::autoincrement_t, void> { - using statement_type = constraints::autoincrement_t; + struct statement_serializator<autoincrement_t, void> { + using statement_type = autoincrement_t; template<class C> - std::string operator()(const statement_type &c, const C &) const { + std::string operator()(const statement_type& c, const C&) const { return static_cast<std::string>(c); } }; template<class... Cs> - struct statement_serializator<constraints::primary_key_t<Cs...>, void> { - using statement_type = constraints::primary_key_t<Cs...>; + struct statement_serializator<primary_key_t<Cs...>, void> { + using statement_type = primary_key_t<Cs...>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto res = static_cast<std::string>(c); using columns_tuple = typename statement_type::columns_tuple; auto columnsCount = std::tuple_size<columns_tuple>::value; if(columnsCount) { res += "("; decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { - res += context.column_name(column); - if(columnIndex < columnsCount - 1) { - res += ", "; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { + if(auto columnNamePointer = context.column_name(column)) { + res += *columnNamePointer; + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } - ++columnIndex; }); res += ")"; } @@ -10165,23 +13890,27 @@ namespace sqlite_orm { }; template<class... Args> - struct statement_serializator<constraints::unique_t<Args...>, void> { - using statement_type = constraints::unique_t<Args...>; + struct statement_serializator<unique_t<Args...>, void> { + using statement_type = unique_t<Args...>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto res = static_cast<std::string>(c); using columns_tuple = typename statement_type::columns_tuple; auto columnsCount = std::tuple_size<columns_tuple>::value; if(columnsCount) { res += "("; decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { - res += context.column_name(column); - if(columnIndex < columnsCount - 1) { - res += ", "; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { + if(auto columnNamePointer = context.column_name(column)) { + res += *columnNamePointer; + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } - ++columnIndex; }); res += ")"; } @@ -10190,38 +13919,42 @@ namespace sqlite_orm { }; template<> - struct statement_serializator<constraints::collate_t, void> { - using statement_type = constraints::collate_t; + struct statement_serializator<collate_constraint_t, void> { + using statement_type = collate_constraint_t; template<class C> - std::string operator()(const statement_type &c, const C &) const { + std::string operator()(const statement_type& c, const C&) const { return static_cast<std::string>(c); } }; template<class T> - struct statement_serializator<constraints::default_t<T>, void> { - using statement_type = constraints::default_t<T>; + struct statement_serializator<default_t<T>, void> { + using statement_type = default_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { return static_cast<std::string>(c) + " (" + serialize(c.value, context) + ")"; } }; template<class... Cs, class... Rs> - struct statement_serializator<constraints::foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>>, void> { - using statement_type = constraints::foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>>; + struct statement_serializator<foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>>, void> { + using statement_type = foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>>; template<class C> - std::string operator()(const statement_type &fk, const C &context) const { + std::string operator()(const statement_type& fk, const C& context) const { std::stringstream ss; std::vector<std::string> columnNames; using columns_type_t = typename std::decay<decltype(fk)>::type::columns_type; constexpr const size_t columnsCount = std::tuple_size<columns_type_t>::value; columnNames.reserve(columnsCount); - iterate_tuple(fk.columns, [&columnNames, &context](auto &v) { - columnNames.push_back(context.impl.column_name(v)); + iterate_tuple(fk.columns, [&columnNames, &context](auto& v) { + if(auto columnNamePointer = context.impl.column_name(v)) { + columnNames.push_back(*columnNamePointer); + } else { + columnNames.push_back({}); + } }); ss << "FOREIGN KEY("; for(size_t i = 0; i < columnNames.size(); ++i) { @@ -10241,8 +13974,12 @@ namespace sqlite_orm { auto refTableName = context.impl.find_table_name(typeid(first_reference_mapped_type)); ss << '\'' << refTableName << '\''; } - iterate_tuple(fk.references, [&referencesNames, &context](auto &v) { - referencesNames.push_back(context.impl.column_name(v)); + iterate_tuple(fk.references, [&referencesNames, &context](auto& v) { + if(auto columnNamePointer = context.impl.column_name(v)) { + referencesNames.push_back(*columnNamePointer); + } else { + referencesNames.push_back({}); + } }); ss << "("; for(size_t i = 0; i < referencesNames.size(); ++i) { @@ -10263,11 +14000,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<constraints::check_t<T>, void> { - using statement_type = constraints::check_t<T>; + struct statement_serializator<check_t<T>, void> { + using statement_type = check_t<T>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { return static_cast<std::string>(c) + " " + serialize(c.expression, context); } }; @@ -10277,7 +14014,7 @@ namespace sqlite_orm { using statement_type = column_t<O, T, G, S, Op...>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << "'" << c.name << "' "; using column_type = typename std::decay<decltype(c)>::type; @@ -10293,12 +14030,12 @@ namespace sqlite_orm { int tupleIndex = 0; iterate_tuple( c.constraints, - [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto &v) { + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto& v) { using constraint_type = typename std::decay<decltype(v)>::type; constraintsStrings.push_back(serialize(v, context)); if(is_primary_key<constraint_type>::value) { primaryKeyIndex = tupleIndex; - } else if(std::is_same<constraints::autoincrement_t, constraint_type>::value) { + } else if(std::is_same<autoincrement_t, constraint_type>::value) { autoincrementIndex = tupleIndex; } ++tupleIndex; @@ -10307,7 +14044,7 @@ namespace sqlite_orm { iter_swap(constraintsStrings.begin() + primaryKeyIndex, constraintsStrings.begin() + autoincrementIndex); } - for(auto &str: constraintsStrings) { + for(auto& str: constraintsStrings) { ss << str << ' '; } } @@ -10323,11 +14060,11 @@ namespace sqlite_orm { using statement_type = remove_all_t<T, Args...>; template<class C> - std::string operator()(const statement_type &rem, const C &context) const { - auto &tImpl = context.impl.template get_impl<T>(); + std::string operator()(const statement_type& rem, const C& context) const { + auto& tImpl = context.impl.template get_impl<T>(); std::stringstream ss; ss << "DELETE FROM '" << tImpl.table.name << "' "; - iterate_tuple(rem.conditions, [&context, &ss](auto &v) { + iterate_tuple(rem.conditions, [&context, &ss](auto& v) { ss << serialize(v, context); }); return ss.str(); @@ -10339,33 +14076,8 @@ namespace sqlite_orm { using statement_type = replace_t<T>; template<class C> - std::string operator()(const statement_type &rep, const C &context) const { - using expression_type = typename std::decay<decltype(rep)>::type; - using object_type = typename expression_object_type<expression_type>::type; - auto &tImpl = context.impl.template get_impl<object_type>(); - std::stringstream ss; - ss << "REPLACE INTO '" << tImpl.table.name << "' ("; - auto columnNames = tImpl.table.column_names(); - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } - } - return ss.str(); + std::string operator()(const statement_type& rep, const C& context) const { + return serialize_replace_range_impl(rep, context, 1); } }; @@ -10374,12 +14086,12 @@ namespace sqlite_orm { using statement_type = insert_explicit<T, Cols...>; template<class C> - std::string operator()(const statement_type &ins, const C &context) const { + std::string operator()(const statement_type& ins, const C& context) const { constexpr const size_t colsCount = std::tuple_size<std::tuple<Cols...>>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); using expression_type = typename std::decay<decltype(ins)>::type; using object_type = typename expression_object_type<expression_type>::type; - auto &tImpl = context.impl.template get_impl<object_type>(); + auto& tImpl = context.impl.template get_impl<object_type>(); std::stringstream ss; ss << "INSERT INTO '" << tImpl.table.name << "' "; std::vector<std::string> columnNames; @@ -10387,7 +14099,7 @@ namespace sqlite_orm { { auto columnsContext = context; columnsContext.skip_table_name = true; - iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto &m) { + iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto& m) { auto columnName = serialize(m, columnsContext); if(!columnName.empty()) { columnNames.push_back(columnName); @@ -10425,52 +14137,77 @@ namespace sqlite_orm { using statement_type = update_t<T>; template<class C> - std::string operator()(const statement_type &upd, const C &context) const { + std::string operator()(const statement_type& upd, const C& context) const { using expression_type = typename std::decay<decltype(upd)>::type; using object_type = typename expression_object_type<expression_type>::type; - auto &tImpl = context.impl.template get_impl<object_type>(); + auto& tImpl = context.impl.template get_impl<object_type>(); std::stringstream ss; - ss << "UPDATE '" << tImpl.table.name << "' SET "; + ss << "UPDATE '" << tImpl.table.name << "' SET"; std::vector<std::string> setColumnNames; - tImpl.table.for_each_column([&setColumnNames](auto &c) { - if(!c.template has<constraints::primary_key_t<>>()) { - setColumnNames.emplace_back(c.name); + tImpl.table.for_each_column([&setColumnNames, &tImpl](auto& column) { + if(!column.template has<primary_key_t<>>() && + !tImpl.table.exists_in_composite_primary_key(column)) { + setColumnNames.emplace_back(column.name); } }); for(size_t i = 0; i < setColumnNames.size(); ++i) { - ss << "\"" << setColumnNames[i] << "\"" + ss << " \"" << setColumnNames[i] << "\"" << " = ?"; if(i < setColumnNames.size() - 1) { ss << ","; } - ss << " "; } - ss << "WHERE "; + ss << " WHERE"; auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << "\"" << primaryKeyColumnNames[i] << "\"" + ss << " \"" << primaryKeyColumnNames[i] << "\"" << " = ?"; if(i < primaryKeyColumnNames.size() - 1) { ss << " AND"; } - ss << " "; } return ss.str(); } }; + template<class... Args> + struct statement_serializator<set_t<Args...>, void> { + using statement_type = set_t<Args...>; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + ss << static_cast<std::string>(statement); + auto assignsCount = std::tuple_size<typename statement_type::assigns_type>::value; + decltype(assignsCount) assignIndex = 0; + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(statement.assigns, + [&ss, &context, &leftContext, &assignIndex, assignsCount](auto& value) { + ss << ' ' << serialize(value.lhs, leftContext); + ss << ' ' << static_cast<std::string>(value) << ' '; + ss << serialize(value.rhs, context); + if(assignIndex < assignsCount - 1) { + ss << ","; + } + ++assignIndex; + }); + return ss.str(); + } + }; + template<class... Args, class... Wargs> struct statement_serializator<update_all_t<set_t<Args...>, Wargs...>, void> { using statement_type = update_all_t<set_t<Args...>, Wargs...>; template<class C> - std::string operator()(const statement_type &upd, const C &context) const { + std::string operator()(const statement_type& upd, const C& context) const { std::stringstream ss; ss << "UPDATE "; - table_name_collector collector{[&context](std::type_index ti) { + table_name_collector collector([&context](std::type_index ti) { return context.impl.find_table_name(ti); - }}; + }); iterate_ast(upd.set.assigns, collector); if(!collector.table_names.empty()) { if(collector.table_names.size() == 1) { @@ -10479,7 +14216,7 @@ namespace sqlite_orm { std::vector<std::string> setPairs; auto leftContext = context; leftContext.skip_table_name = true; - iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto &asgn) { + iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto& asgn) { std::stringstream sss; sss << serialize(asgn.lhs, leftContext); sss << " " << static_cast<std::string>(asgn) << " "; @@ -10493,7 +14230,7 @@ namespace sqlite_orm { ss << ", "; } } - iterate_tuple(upd.conditions, [&context, &ss](auto &v) { + iterate_tuple(upd.conditions, [&context, &ss](auto& v) { ss << serialize(v, context); }); return ss.str(); @@ -10506,55 +14243,148 @@ namespace sqlite_orm { } }; + template<class T, class C> + std::string serialize_insert_range_impl(const T& /*statement*/, const C& context, const int valuesCount) { + using object_type = typename expression_object_type<T>::type; + auto& tImpl = context.impl.template get_impl<object_type>(); + + std::stringstream ss; + ss << "INSERT INTO '" << tImpl.table.name << "' "; + std::vector<std::string> columnNames; + auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); + + tImpl.table.for_each_column([&columnNames, &compositeKeyColumnNames](auto& c) { + using table_type = typename std::decay<decltype(tImpl.table)>::type; + if(table_type::is_without_rowid || !c.template has<primary_key_t<>>()) { + auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + if(it == compositeKeyColumnNames.end()) { + columnNames.emplace_back(c.name); + } + } + }); + + const auto columnNamesCount = columnNames.size(); + if(columnNamesCount) { + ss << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + } else { + ss << "DEFAULT "; + } + ss << "VALUES "; + if(columnNamesCount) { + auto valuesString = [columnNamesCount] { + std::stringstream ss_; + ss_ << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss_ << "?"; + if(i < columnNamesCount - 1) { + ss_ << ", "; + } else { + ss_ << ")"; + } + } + return ss_.str(); + }(); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; + } + ss << " "; + } + } else if(valuesCount != 1) { + throw std::system_error(std::make_error_code(orm_error_code::cannot_use_default_value)); + } + + return ss.str(); + } + template<class T> struct statement_serializator<insert_t<T>, void> { using statement_type = insert_t<T>; template<class C> - std::string operator()(const statement_type &, const C &context) const { - using object_type = typename expression_object_type<statement_type>::type; - auto &tImpl = context.impl.template get_impl<object_type>(); + std::string operator()(const statement_type& statement, const C& context) const { + return serialize_insert_range_impl(statement, context, 1); + } + }; + + template<class T> + struct statement_serializator<into_t<T>, void> { + using statement_type = into_t<T>; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' "; - std::vector<std::string> columnNames; - auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); + auto& tImpl = context.impl.template get_impl<T>(); + ss << "INTO " << tImpl.table.name; + return ss.str(); + } + }; - tImpl.table.for_each_column([&tImpl, &columnNames, &compositeKeyColumnNames](auto &c) { - if(tImpl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()) { - auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - columnNames.emplace_back(c.name); - } + template<class... Args> + struct statement_serializator<columns_t<Args...>, void> { + using statement_type = columns_t<Args...>; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + auto index = 0; + if(context.use_parentheses) { + ss << '('; + } + iterate_tuple(statement.columns, [&context, &ss, &index](auto& value) { + ss << serialize(value, context); + if(index < int(std::tuple_size<std::tuple<Args...>>::value) - 1) { + ss << ", "; } + ++index; }); + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; - auto columnNamesCount = columnNames.size(); - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } + template<class T> + struct statement_serializator< + T, + typename std::enable_if<is_insert_raw<T>::value || is_replace_raw<T>::value>::type> { + using statement_type = T; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + if(is_insert_raw<T>::value) { + ss << "INSERT"; } else { - ss << "DEFAULT "; + ss << "REPLACE"; } - ss << "VALUES "; - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } + iterate_tuple(statement.args, [&context, &ss](auto& value) { + using value_type = typename std::decay<decltype(value)>::type; + ss << ' '; + if(is_columns<value_type>::value) { + auto newContext = context; + newContext.skip_table_name = true; + newContext.use_parentheses = true; + ss << serialize(value, newContext); + } else if(is_values<value_type>::value || is_select<value_type>::value) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(value, newContext); + } else { + ss << serialize(value, context); } - } + }); return ss.str(); } }; @@ -10564,8 +14394,8 @@ namespace sqlite_orm { using statement_type = remove_t<T, Ids...>; template<class C> - std::string operator()(const statement_type &, const C &context) const { - auto &tImpl = context.impl.template get_impl<T>(); + std::string operator()(const statement_type&, const C& context) const { + auto& tImpl = context.impl.template get_impl<T>(); std::stringstream ss; ss << "DELETE FROM '" << tImpl.table.name << "' "; ss << "WHERE "; @@ -10581,110 +14411,73 @@ namespace sqlite_orm { } }; - template<class It> - struct statement_serializator<replace_range_t<It>, void> { - using statement_type = replace_range_t<It>; + template<class T, class C> + std::string serialize_replace_range_impl(const T& rep, const C& context, const int valuesCount) { + using expression_type = typename std::decay<decltype(rep)>::type; + using object_type = typename expression_object_type<expression_type>::type; + auto& tImpl = context.impl.template get_impl<object_type>(); + std::stringstream ss; + ss << "REPLACE INTO '" << tImpl.table.name << "' ("; - template<class C> - std::string operator()(const statement_type &rep, const C &context) const { - using expression_type = typename std::decay<decltype(rep)>::type; - using object_type = typename expression_type::object_type; - auto &tImpl = context.impl.template get_impl<object_type>(); - std::stringstream ss; - ss << "REPLACE INTO '" << tImpl.table.name << "' ("; - auto columnNames = tImpl.table.column_names(); - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ", "; + auto columnIndex = 0; + auto columnsCount = tImpl.table.count_columns_amount(); + tImpl.table.for_each_column([&ss, &columnIndex, columnsCount](auto& column) { + ss << " \"" << column.name << "\""; + if(columnIndex < columnsCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ++columnIndex; + }); + ss << " VALUES "; + auto valuesString = [columnsCount] { + std::stringstream ss_; + ss_ << "("; + for(auto i = 0; i < columnsCount; ++i) { + ss_ << "?"; + if(i < columnsCount - 1) { + ss_ << ", "; } else { - ss << ") "; + ss_ << ")"; } } - ss << "VALUES "; - auto valuesString = [columnNamesCount] { - std::stringstream ss_; - ss_ << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss_ << "?"; - if(i < columnNamesCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - auto valuesCount = static_cast<int>(std::distance(rep.range.first, rep.range.second)); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; + return ss_.str(); + }(); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; } - return ss.str(); + ss << " "; } - }; + return ss.str(); + } - template<class It> - struct statement_serializator<insert_range_t<It>, void> { - using statement_type = insert_range_t<It>; + template<class It, class L, class O> + struct statement_serializator<replace_range_t<It, L, O>, void> { + using statement_type = replace_range_t<It, L, O>; template<class C> - std::string operator()(const statement_type &statement, const C &context) const { - using expression_type = typename std::decay<decltype(statement)>::type; - using object_type = typename expression_type::object_type; - auto &tImpl = context.impl.template get_impl<object_type>(); + std::string operator()(const statement_type& rep, const C& context) const { + auto valuesCount = static_cast<int>(std::distance(rep.range.first, rep.range.second)); + return serialize_replace_range_impl(rep, context, valuesCount); + } + }; - std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' ("; - std::vector<std::string> columnNames; - tImpl.table.for_each_column([&columnNames](auto &c) { - if(!c.template has<constraints::primary_key_t<>>()) { - columnNames.emplace_back(c.name); - } - }); + template<class It, class L, class O> + struct statement_serializator<insert_range_t<It, L, O>, void> { + using statement_type = insert_range_t<It, L, O>; - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES "; - auto valuesString = [columnNamesCount] { - std::stringstream ss_; - ss_ << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss_ << "?"; - if(i < columnNamesCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - auto valuesCount = static_cast<int>(std::distance(statement.range.first, statement.range.second)); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; - } - return ss.str(); + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { + const auto valuesCount = static_cast<int>(std::distance(statement.range.first, statement.range.second)); + return serialize_insert_range_impl(statement, context, valuesCount); } }; template<class T, class C> - std::string serialize_get_all_impl(const T &get, const C &context) { + std::string serialize_get_all_impl(const T& get, const C& context) { using primary_type = typename T::type; table_name_collector collector; @@ -10692,23 +14485,23 @@ namespace sqlite_orm { std::make_pair(context.impl.find_table_name(typeid(primary_type)), std::string{})); iterate_ast(get.conditions, collector); std::stringstream ss; - ss << "SELECT "; - auto &tImpl = context.impl.template get_impl<primary_type>(); - auto columnNames = tImpl.table.column_names(); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "\"" << tImpl.table.name << "\"." - << "\"" << columnNames[i] << "\""; - if(i < columnNames.size() - 1) { - ss << ", "; - } else { - ss << " "; + ss << "SELECT"; + auto& tImpl = context.impl.template get_impl<primary_type>(); + auto columnIndex = 0; + auto columnsCount = tImpl.table.count_columns_amount(); + tImpl.table.for_each_column([&ss, &columnIndex, columnsCount, &tImpl](auto& column) { + ss << " \"" << tImpl.table.name << "\"." + << "\"" << column.name << "\""; + if(columnIndex < columnsCount - 1) { + ss << ","; } - } - ss << "FROM "; + ++columnIndex; + }); + ss << " FROM "; std::vector<std::pair<std::string, std::string>> tableNames(collector.table_names.begin(), collector.table_names.end()); for(size_t i = 0; i < tableNames.size(); ++i) { - auto &tableNamePair = tableNames[i]; + auto& tableNamePair = tableNames[i]; ss << "'" << tableNamePair.first << "' "; if(!tableNamePair.second.empty()) { ss << tableNamePair.second << " "; @@ -10718,7 +14511,7 @@ namespace sqlite_orm { } ss << " "; } - iterate_tuple(get.conditions, [&context, &ss](auto &v) { + iterate_tuple(get.conditions, [&context, &ss](auto& v) { ss << serialize(v, context); }); return ss.str(); @@ -10730,7 +14523,7 @@ namespace sqlite_orm { using statement_type = get_all_optional_t<T, R, Args...>; template<class C> - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_all_impl(get, context); } }; @@ -10741,7 +14534,7 @@ namespace sqlite_orm { using statement_type = get_all_pointer_t<T, R, Args...>; template<class C> - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_all_impl(get, context); } }; @@ -10751,35 +14544,35 @@ namespace sqlite_orm { using statement_type = get_all_t<T, R, Args...>; template<class C> - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_all_impl(get, context); } }; template<class T, class C> - std::string serialize_get_impl(const T &, const C &context) { + std::string serialize_get_impl(const T&, const C& context) { using primary_type = typename T::type; - auto &tImpl = context.impl.template get_impl<primary_type>(); + auto& tImpl = context.impl.template get_impl<primary_type>(); std::stringstream ss; - ss << "SELECT "; - auto columnNames = tImpl.table.column_names(); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNames.size() - 1) { - ss << ","; + ss << "SELECT"; + auto columnIndex = 0; + auto columnsCount = tImpl.table.count_columns_amount(); + tImpl.table.for_each_column([&ss, &columnIndex, columnsCount](auto& column) { + ss << " \"" << column.name << "\""; + if(columnIndex < columnsCount - 1) { + ss << ", "; } - ss << " "; - } - ss << "FROM '" << tImpl.table.name << "' WHERE "; + ++columnIndex; + }); + ss << " FROM '" << tImpl.table.name << "' WHERE "; auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); if(!primaryKeyColumnNames.empty()) { for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << "\"" << primaryKeyColumnNames[i] << "\"" - << " = ? "; + ss << " \"" << primaryKeyColumnNames[i] << "\"" + << " = ?"; if(i < primaryKeyColumnNames.size() - 1) { - ss << "AND"; + ss << " AND"; } - ss << ' '; } return ss.str(); } else { @@ -10792,7 +14585,7 @@ namespace sqlite_orm { using statement_type = get_t<T, Ids...>; template<class C> - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_impl(get, context); } }; @@ -10802,8 +14595,29 @@ namespace sqlite_orm { using statement_type = get_pointer_t<T, Ids...>; template<class C> - std::string operator()(const statement_type &get, const C &context) const { - return serialize_get_impl(get, context); + std::string operator()(const statement_type& statement, const C& context) const { + return serialize_get_impl(statement, context); + } + }; + + template<> + struct statement_serializator<insert_constraint, void> { + using statement_type = insert_constraint; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { + switch(statement) { + case insert_constraint::abort: + return "OR ABORT"; + case insert_constraint::fail: + return "OR FAIL"; + case insert_constraint::ignore: + return "OR IGNORE"; + case insert_constraint::replace: + return "OR REPLACE"; + case insert_constraint::rollback: + return "OR ROLLBACK"; + } } }; @@ -10813,7 +14627,7 @@ namespace sqlite_orm { using statement_type = get_optional_t<T, Ids...>; template<class C> - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_impl(get, context); } }; @@ -10823,11 +14637,12 @@ namespace sqlite_orm { using statement_type = select_t<T, Args...>; template<class C> - std::string operator()(const statement_type &sel, const C &context) const { + std::string operator()(const statement_type& sel, const C& context) const { std::stringstream ss; - if(!is_base_of_template<T, compound_operator>::value) { - if(!sel.highest_level) { - ss << "( "; + const auto isCompoundOperator = is_base_of_template<T, compound_operator>::value; + if(!isCompoundOperator) { + if(!sel.highest_level && context.use_parentheses) { + ss << "("; } ss << "SELECT "; } @@ -10838,46 +14653,47 @@ namespace sqlite_orm { for(size_t i = 0; i < columnNames.size(); ++i) { ss << columnNames[i]; if(i < columnNames.size() - 1) { - ss << ","; + ss << ", "; } - ss << " "; } - table_name_collector collector{[&context](std::type_index ti) { + table_name_collector collector([&context](std::type_index ti) { return context.impl.find_table_name(ti); - }}; - iterate_ast(sel.col, collector); - iterate_ast(sel.conditions, collector); - internal::join_iterator<Args...>()([&collector, &context](const auto &c) { - using original_join_type = typename std::decay<decltype(c)>::type::join_type::type; - using cross_join_type = typename internal::mapped_type_proxy<original_join_type>::type; - auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); - auto tableAliasString = alias_extractor<original_join_type>::get(); - std::pair<std::string, std::string> tableNameWithAlias(std::move(crossJoinedTableName), - std::move(tableAliasString)); - collector.table_names.erase(tableNameWithAlias); }); - if(!collector.table_names.empty()) { - ss << "FROM "; - std::vector<std::pair<std::string, std::string>> tableNames(collector.table_names.begin(), - collector.table_names.end()); - for(size_t i = 0; i < tableNames.size(); ++i) { - auto &tableNamePair = tableNames[i]; - ss << "'" << tableNamePair.first << "' "; - if(!tableNamePair.second.empty()) { - ss << tableNamePair.second << " "; - } - if(int(i) < int(tableNames.size()) - 1) { - ss << ","; + const auto explicitFromItemsCount = count_tuple<std::tuple<Args...>, is_from>::value; + if(!explicitFromItemsCount) { + iterate_ast(sel.col, collector); + iterate_ast(sel.conditions, collector); + join_iterator<Args...>()([&collector, &context](const auto& c) { + using original_join_type = typename std::decay<decltype(c)>::type::join_type::type; + using cross_join_type = typename internal::mapped_type_proxy<original_join_type>::type; + auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); + auto tableAliasString = alias_extractor<original_join_type>::get(); + std::pair<std::string, std::string> tableNameWithAlias(std::move(crossJoinedTableName), + std::move(tableAliasString)); + collector.table_names.erase(tableNameWithAlias); + }); + if(!collector.table_names.empty() && !isCompoundOperator) { + ss << " FROM "; + std::vector<std::pair<std::string, std::string>> tableNames(collector.table_names.begin(), + collector.table_names.end()); + for(size_t i = 0; i < tableNames.size(); ++i) { + auto& tableNamePair = tableNames[i]; + ss << "'" << tableNamePair.first << "'"; + if(!tableNamePair.second.empty()) { + ss << ' ' << tableNamePair.second; + } + if(int(i) < int(tableNames.size()) - 1) { + ss << ", "; + } } - ss << " "; } } - iterate_tuple(sel.conditions, [&context, &ss](auto &v) { - ss << serialize(v, context); + iterate_tuple(sel.conditions, [&context, &ss](auto& v) { + ss << ' ' << serialize(v, context); }); if(!is_base_of_template<T, compound_operator>::value) { - if(!sel.highest_level) { - ss << ") "; + if(!sel.highest_level && context.use_parentheses) { + ss << ")"; } } return ss.str(); @@ -10889,7 +14705,7 @@ namespace sqlite_orm { using statement_type = indexed_column_t<T>; template<class C> - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; ss << serialize(statement.column_or_expression, context); if(!statement._collation_name.empty()) { @@ -10916,23 +14732,24 @@ namespace sqlite_orm { using statement_type = index_t<Cols...>; template<class C> - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; ss << "CREATE "; if(statement.unique) { ss << "UNIQUE "; } - using columns_type = typename std::decay<decltype(statement)>::type::columns_type; - using head_t = typename std::tuple_element<0, columns_type>::type::column_type; + using elements_type = typename std::decay<decltype(statement)>::type::elements_type; + using head_t = typename std::tuple_element<0, elements_type>::type::column_type; using indexed_type = typename table_type<head_t>::type; ss << "INDEX IF NOT EXISTS '" << statement.name << "' ON '" << context.impl.find_table_name(typeid(indexed_type)) << "' ("; std::vector<std::string> columnNames; - iterate_tuple(statement.columns, [&columnNames, &context](auto &v) { - columnNames.push_back(context.column_name(v.column_or_expression)); + iterate_tuple(statement.elements, [&columnNames, &context](auto& v) { + auto columnName = serialize(v, context); + columnNames.push_back(move(columnName)); }); for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "'" << columnNames[i] << "'"; + ss << columnNames[i]; if(i < columnNames.size() - 1) { ss << ", "; } @@ -10942,15 +14759,44 @@ namespace sqlite_orm { } }; + template<class... Args> + struct statement_serializator<from_t<Args...>, void> { + using statement_type = from_t<Args...>; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { + using tuple = std::tuple<Args...>; + + std::stringstream ss; + ss << "FROM "; + size_t index = 0; + iterate_tuple<tuple>([&context, &ss, &index](auto* itemPointer) { + using mapped_type = typename std::remove_pointer<decltype(itemPointer)>::type; + + auto aliasString = alias_extractor<mapped_type>::get(); + ss << "'" << context.impl.find_table_name(typeid(typename mapped_type_proxy<mapped_type>::type)) + << "'"; + if(aliasString.length()) { + ss << " '" << aliasString << "'"; + } + if(index < std::tuple_size<tuple>::value - 1) { + ss << ", "; + } + ++index; + }); + return ss.str(); + } + }; + template<class T> struct statement_serializator<where_t<T>, void> { using statement_type = where_t<T>; template<class C> - std::string operator()(const statement_type &w, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; - ss << static_cast<std::string>(w) << " "; - auto whereString = serialize(w.c, context); + ss << statement.serialize() << " "; + auto whereString = serialize(statement.expression, context); ss << "( " << whereString << ") "; return ss.str(); } @@ -10961,7 +14807,7 @@ namespace sqlite_orm { using statement_type = order_by_t<O>; template<class C> - std::string operator()(const statement_type &orderBy, const C &context) const { + std::string operator()(const statement_type& orderBy, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(orderBy) << " "; auto orderByString = serialize_order_by(orderBy, context); @@ -10975,7 +14821,7 @@ namespace sqlite_orm { using statement_type = dynamic_order_by_t<C>; template<class CC> - std::string operator()(const statement_type &orderBy, const CC &context) const { + std::string operator()(const statement_type& orderBy, const CC& context) const { return serialize_order_by(orderBy, context); } }; @@ -10985,10 +14831,10 @@ namespace sqlite_orm { using statement_type = multi_order_by_t<Args...>; template<class C> - std::string operator()(const statement_type &orderBy, const C &context) const { + std::string operator()(const statement_type& orderBy, const C& context) const { std::stringstream ss; std::vector<std::string> expressions; - iterate_tuple(orderBy.args, [&expressions, &context](auto &v) { + iterate_tuple(orderBy.args, [&expressions, &context](auto& v) { auto expression = serialize_order_by(v, context); expressions.push_back(move(expression)); }); @@ -11009,7 +14855,7 @@ namespace sqlite_orm { using statement_type = cross_join_t<O>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(c) << " "; ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; @@ -11022,7 +14868,7 @@ namespace sqlite_orm { using statement_type = inner_join_t<T, O>; template<class C> - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(l) << " "; auto aliasString = alias_extractor<T>::get(); @@ -11040,7 +14886,7 @@ namespace sqlite_orm { using statement_type = on_t<T>; template<class C> - std::string operator()(const statement_type &t, const C &context) const { + std::string operator()(const statement_type& t, const C& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; @@ -11054,10 +14900,14 @@ namespace sqlite_orm { using statement_type = join_t<T, O>; template<class C> - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(l) << " "; - ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + auto aliasString = alias_extractor<T>::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy<T>::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } ss << serialize(l.constraint, context); return ss.str(); } @@ -11068,10 +14918,14 @@ namespace sqlite_orm { using statement_type = left_join_t<T, O>; template<class C> - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(l) << " "; - ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + auto aliasString = alias_extractor<T>::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy<T>::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } ss << serialize(l.constraint, context); return ss.str(); } @@ -11082,10 +14936,14 @@ namespace sqlite_orm { using statement_type = left_outer_join_t<T, O>; template<class C> - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(l) << " "; - ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + auto aliasString = alias_extractor<T>::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy<T>::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } ss << serialize(l.constraint, context); return ss.str(); } @@ -11096,7 +14954,7 @@ namespace sqlite_orm { using statement_type = natural_join_t<O>; template<class C> - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast<std::string>(c) << " "; ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; @@ -11109,12 +14967,12 @@ namespace sqlite_orm { using statement_type = group_by_t<Args...>; template<class C> - std::string operator()(const statement_type &groupBy, const C &context) const { + std::string operator()(const statement_type& groupBy, const C& context) const { std::stringstream ss; std::vector<std::string> expressions; auto newContext = context; newContext.skip_table_name = false; - iterate_tuple(groupBy.args, [&expressions, &newContext](auto &v) { + iterate_tuple(groupBy.args, [&expressions, &newContext](auto& v) { auto expression = serialize(v, newContext); expressions.push_back(expression); }); @@ -11135,7 +14993,7 @@ namespace sqlite_orm { using statement_type = having_t<T>; template<class C> - std::string operator()(const statement_type &hav, const C &context) const { + std::string operator()(const statement_type& hav, const C& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; @@ -11154,21 +15012,21 @@ namespace sqlite_orm { using statement_type = limit_t<T, HO, OI, O>; template<class C> - std::string operator()(const statement_type &limt, const C &context) const { + std::string operator()(const statement_type& limt, const C& context) const { auto newContext = context; newContext.skip_table_name = false; std::stringstream ss; ss << static_cast<std::string>(limt) << " "; if(HO) { if(OI) { - limt.off.apply([&newContext, &ss](auto &value) { + limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); ss << ", "; ss << serialize(limt.lim, newContext); } else { ss << serialize(limt.lim, newContext) << " OFFSET "; - limt.off.apply([&newContext, &ss](auto &value) { + limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); } @@ -11179,12 +15037,22 @@ namespace sqlite_orm { } }; + template<> + struct statement_serializator<default_values_t, void> { + using statement_type = default_values_t; + + template<class C> + std::string operator()(const statement_type& statement, const C& context) const { + return "DEFAULT VALUES"; + } + }; + template<class F, class O> struct statement_serializator<using_t<F, O>, void> { using statement_type = using_t<F, O>; template<class C> - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { auto newContext = context; newContext.skip_table_name = true; return static_cast<std::string>(statement) + " (" + serialize(statement.column, newContext) + " )"; @@ -11196,12 +15064,12 @@ namespace sqlite_orm { using statement_type = std::tuple<Args...>; template<class C> - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; ss << '('; auto index = 0; using TupleSize = std::tuple_size<statement_type>; - iterate_tuple(statement, [&context, &index, &ss](auto &value) { + iterate_tuple(statement, [&context, &index, &ss](auto& value) { ss << serialize(value, context); if(index < TupleSize::value - 1) { ss << ", "; @@ -11218,7 +15086,7 @@ namespace sqlite_orm { using statement_type = values_t<Args...>; template<class C> - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; @@ -11226,10 +15094,10 @@ namespace sqlite_orm { ss << "VALUES "; { auto index = 0; - auto &tuple = statement.tuple; + auto& tuple = statement.tuple; using tuple_type = typename std::decay<decltype(tuple)>::type; using TupleSize = std::tuple_size<tuple_type>; - iterate_tuple(tuple, [&context, &index, &ss](auto &value) { + iterate_tuple(tuple, [&context, &index, &ss](auto& value) { ss << serialize(value, context); if(index < TupleSize::value - 1) { ss << ", "; @@ -11249,7 +15117,7 @@ namespace sqlite_orm { using statement_type = dynamic_values_t<T>; template<class C> - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; @@ -11258,7 +15126,7 @@ namespace sqlite_orm { { auto vectorSize = statement.vector.size(); for(decltype(vectorSize) index = 0; index < vectorSize; ++index) { - auto &value = statement.vector[index]; + auto& value = statement.vector[index]; ss << serialize(value, context); if(index < vectorSize - 1) { ss << ", "; @@ -11279,6 +15147,10 @@ namespace sqlite_orm { // #include "object_from_column_builder.h" +// #include "table.h" + +// #include "column.h" + namespace sqlite_orm { namespace internal { @@ -11296,10 +15168,10 @@ namespace sqlite_orm { * @param filename database filename. * @param impl_ storage_impl head */ - storage_t(const std::string &filename, impl_type impl_) : + storage_t(const std::string& filename, impl_type impl_) : storage_base{filename, foreign_keys_count(impl_)}, impl(std::move(impl_)) {} - storage_t(const storage_t &other) : storage_base(other), impl(other.impl) {} + storage_t(const storage_t& other) : storage_base(other), impl(other.impl) {} protected: impl_type impl; @@ -11317,42 +15189,30 @@ namespace sqlite_orm { friend struct serializator_context_builder; template<class I> - void create_table(sqlite3 *db, const std::string &tableName, const I &tableImpl) { + void create_table(sqlite3* db, const std::string& tableName, const I& tableImpl) { + using table_type = typename std::decay<decltype(tableImpl.table)>::type; std::stringstream ss; ss << "CREATE TABLE '" << tableName << "' ( "; - auto columnsCount = tableImpl.table.columns_count; + auto elementsCount = tableImpl.table.elements_count; auto index = 0; using context_t = serializator_context<impl_type>; context_t context{this->impl}; - iterate_tuple(tableImpl.table.columns, [columnsCount, &index, &ss, &context](auto &c) { - ss << serialize(c, context); - if(index < columnsCount - 1) { + iterate_tuple(tableImpl.table.elements, [elementsCount, &index, &ss, &context](auto& element) { + ss << serialize(element, context); + if(index < elementsCount - 1) { ss << ", "; } index++; }); - ss << ") "; - if(tableImpl.table._without_rowid) { - ss << "WITHOUT ROWID "; - } - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + ss << ")"; + if(table_type::is_without_rowid) { + ss << " WITHOUT ROWID"; } + perform_void_exec(db, ss.str()); } template<class I> - void backup_table(sqlite3 *db, const I &tableImpl, const std::vector<table_info *> &columnsToIgnore) { + void backup_table(sqlite3* db, const I& tableImpl, const std::vector<table_info*>& columnsToIgnore) { // here we copy source table to another with a name with '_backup' suffix, but in case table with such // a name already exists we append suffix 1, then 2, etc until we find a free name.. @@ -11387,18 +15247,43 @@ namespace sqlite_orm { } template<class O> - auto &get_impl() const { + void assert_insertable_type() const { + auto& tImpl = this->get_impl<O>(); + using table_type = typename std::decay<decltype(tImpl.table)>::type; + using elements_type = typename std::decay<decltype(tImpl.table.elements)>::type; + + using is_without_rowid = std::integral_constant<bool, table_type::is_without_rowid>; + + static_if<is_without_rowid{}>( + [](auto&) {}, // all right. it's a "without_rowid" table + [](auto& tImpl) { // unfortunately, this static_assert's can't see an composite keys(( + std::ignore = tImpl; + static_assert( + count_tuple<elements_type, is_column_with_insertable_primary_key>::value <= 1, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " + "'insert', or you can use 'insert' with explicit column listing."); + static_assert( + count_tuple<elements_type, is_column_with_noninsertable_primary_key>::value == 0, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " + "of 'insert', or you can use 'insert' with explicit column listing."); + })(tImpl); + } + + template<class O> + auto& get_impl() const { return this->impl.template get_impl<O>(); } template<class O> - auto &get_impl() { + auto& get_impl() { return this->impl.template get_impl<O>(); } public: template<class T, class... Args> - view_t<T, self, Args...> iterate(Args &&... args) { + view_t<T, self, Args...> iterate(Args&&... args) { this->assert_mapped_type<T>(); auto con = this->get_connection(); @@ -11413,7 +15298,7 @@ namespace sqlite_orm { * @example: storage.remove_all<User>(where(in(&User::id, {5, 6, 7}))); - DELETE FROM users WHERE id IN (5, 6, 7) */ template<class O, class... Args> - void remove_all(Args &&... args) { + void remove_all(Args&&... args) { this->assert_mapped_type<O>(); auto statement = this->prepare(sqlite_orm::remove_all<O>(std::forward<Args>(args)...)); this->execute(statement); @@ -11438,7 +15323,7 @@ namespace sqlite_orm { * @param o object to be updated. */ template<class O> - void update(const O &o) { + void update(const O& o) { this->assert_mapped_type<O>(); auto statement = this->prepare(sqlite_orm::update(std::ref(o))); this->execute(statement); @@ -11452,7 +15337,7 @@ namespace sqlite_orm { protected: template<class F, class O, class... Args> - std::string group_concat_internal(F O::*m, std::unique_ptr<std::string> y, Args &&... args) { + std::string group_concat_internal(F O::*m, std::unique_ptr<std::string> y, Args&&... args) { this->assert_mapped_type<O>(); std::vector<std::string> rows; if(y) { @@ -11477,7 +15362,7 @@ namespace sqlite_orm { * @example: storage.get_all<User>(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id */ template<class O, class... Args> - auto get_all(Args &&... args) { + auto get_all(Args&&... args) { this->assert_mapped_type<O>(); auto statement = this->prepare(sqlite_orm::get_all<O>(std::forward<Args>(args)...)); return this->execute(statement); @@ -11492,7 +15377,7 @@ namespace sqlite_orm { * @example: storage.get_all<User, std::list<User>>(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id */ template<class O, class R, class... Args> - auto get_all(Args &&... args) { + auto get_all(Args&&... args) { this->assert_mapped_type<O>(); auto statement = this->prepare(sqlite_orm::get_all<O, R>(std::forward<Args>(args)...)); return this->execute(statement); @@ -11507,7 +15392,7 @@ namespace sqlite_orm { * @example: storage.get_all_pointer<User>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 */ template<class O, class... Args> - auto get_all_pointer(Args &&... args) { + auto get_all_pointer(Args&&... args) { this->assert_mapped_type<O>(); auto statement = this->prepare(sqlite_orm::get_all_pointer<O>(std::forward<Args>(args)...)); return this->execute(statement); @@ -11522,7 +15407,7 @@ namespace sqlite_orm { * @example: storage.get_all_pointer<User, std::list<User>>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 */ template<class O, class R, class... Args> - auto get_all_pointer(Args &&... args) { + auto get_all_pointer(Args&&... args) { this->assert_mapped_type<O>(); auto statement = this->prepare(sqlite_orm::get_all_pointer<O, R>(std::forward<Args>(args)...)); return this->execute(statement); @@ -11590,7 +15475,7 @@ namespace sqlite_orm { * @return Number of O object in table. */ template<class O, class... Args, class R = typename mapped_type_proxy<O>::type> - int count(Args &&... args) { + int count(Args&&... args) { this->assert_mapped_type<R>(); auto rows = this->select(sqlite_orm::count<R>(), std::forward<Args>(args)...); if(!rows.empty()) { @@ -11603,9 +15488,10 @@ namespace sqlite_orm { /** * SELECT COUNT(X) https://www.sqlite.org/lang_aggfunc.html#count * @param m member pointer to class mapped to the storage. + * @return count of `m` values from database. */ template<class F, class O, class... Args> - int count(F O::*m, Args &&... args) { + int count(F O::*m, Args&&... args) { this->assert_mapped_type<O>(); auto rows = this->select(sqlite_orm::count(m), std::forward<Args>(args)...); if(!rows.empty()) { @@ -11618,10 +15504,10 @@ namespace sqlite_orm { /** * AVG(X) query. https://www.sqlite.org/lang_aggfunc.html#avg * @param m is a class member pointer (the same you passed into make_column). - * @return average value from db. + * @return average value from database. */ template<class F, class O, class... Args> - double avg(F O::*m, Args &&... args) { + double avg(F O::*m, Args&&... args) { this->assert_mapped_type<O>(); auto rows = this->select(sqlite_orm::avg(m), std::forward<Args>(args)...); if(!rows.empty()) { @@ -11646,7 +15532,7 @@ namespace sqlite_orm { class... Args, class Tuple = std::tuple<Args...>, typename sfinae = typename std::enable_if<std::tuple_size<Tuple>::value >= 1>::type> - std::string group_concat(F O::*m, Args &&... args) { + std::string group_concat(F O::*m, Args&&... args) { return this->group_concat_internal(m, {}, std::forward<Args>(args)...); } @@ -11656,14 +15542,14 @@ namespace sqlite_orm { * @return group_concat query result. */ template<class F, class O, class... Args> - std::string group_concat(F O::*m, std::string y, Args &&... args) { + std::string group_concat(F O::*m, std::string y, Args&&... args) { return this->group_concat_internal(m, std::make_unique<std::string>(move(y)), std::forward<Args>(args)...); } template<class F, class O, class... Args> - std::string group_concat(F O::*m, const char *y, Args &&... args) { + std::string group_concat(F O::*m, const char* y, Args&&... args) { std::unique_ptr<std::string> str; if(y) { str = std::make_unique<std::string>(y); @@ -11679,7 +15565,7 @@ namespace sqlite_orm { * @return std::unique_ptr with max value or null if sqlite engine returned null. */ template<class F, class O, class... Args, class Ret = typename column_result_t<self, F O::*>::type> - std::unique_ptr<Ret> max(F O::*m, Args &&... args) { + std::unique_ptr<Ret> max(F O::*m, Args&&... args) { this->assert_mapped_type<O>(); auto rows = this->select(sqlite_orm::max(m), std::forward<Args>(args)...); if(!rows.empty()) { @@ -11695,7 +15581,7 @@ namespace sqlite_orm { * @return std::unique_ptr with min value or null if sqlite engine returned null. */ template<class F, class O, class... Args, class Ret = typename column_result_t<self, F O::*>::type> - std::unique_ptr<Ret> min(F O::*m, Args &&... args) { + std::unique_ptr<Ret> min(F O::*m, Args&&... args) { this->assert_mapped_type<O>(); auto rows = this->select(sqlite_orm::min(m), std::forward<Args>(args)...); if(!rows.empty()) { @@ -11711,7 +15597,7 @@ namespace sqlite_orm { * @return std::unique_ptr with sum value or null if sqlite engine returned null. */ template<class F, class O, class... Args, class Ret = typename column_result_t<self, F O::*>::type> - std::unique_ptr<Ret> sum(F O::*m, Args &&... args) { + std::unique_ptr<Ret> sum(F O::*m, Args&&... args) { this->assert_mapped_type<O>(); std::vector<std::unique_ptr<double>> rows = this->select(sqlite_orm::sum(m), std::forward<Args>(args)...); @@ -11733,7 +15619,7 @@ namespace sqlite_orm { * https://www.sqlite.org/lang_aggfunc.html) */ template<class F, class O, class... Args> - double total(F O::*m, Args &&... args) { + double total(F O::*m, Args&&... args) { this->assert_mapped_type<O>(); auto rows = this->select(sqlite_orm::total(m), std::forward<Args>(args)...); if(!rows.empty()) { @@ -11759,7 +15645,7 @@ namespace sqlite_orm { template<class T> typename std::enable_if<is_prepared_statement<T>::value, std::string>::type - dump(const T &preparedStatement) const { + dump(const T& preparedStatement) const { using context_t = serializator_context<impl_type>; context_t context{this->impl}; return serialize(preparedStatement.t, context); @@ -11771,13 +15657,13 @@ namespace sqlite_orm { */ template<class O> typename std::enable_if<storage_traits::type_is_mapped<self, O>::value, std::string>::type - dump(const O &o) { - auto &tImpl = this->get_impl<O>(); + dump(const O& o) { + auto& tImpl = this->get_impl<O>(); std::stringstream ss; ss << "{ "; using pair = std::pair<std::string, std::string>; std::vector<pair> pairs; - tImpl.table.for_each_column([&pairs, &o](auto &c) { + tImpl.table.for_each_column([&pairs, &o](auto& c) { using column_type = typename std::decay<decltype(c)>::type; using field_type = typename column_type::field_type; pair p{c.name, std::string()}; @@ -11791,7 +15677,7 @@ namespace sqlite_orm { pairs.push_back(move(p)); }); for(size_t i = 0; i < pairs.size(); ++i) { - auto &p = pairs[i]; + auto& p = pairs[i]; ss << p.first << " : '" << p.second << "'"; if(i < pairs.size() - 1) { ss << ", "; @@ -11809,7 +15695,7 @@ namespace sqlite_orm { * id and creates own one. */ template<class O> - void replace(const O &o) { + void replace(const O& o) { this->assert_mapped_type<O>(); auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); this->execute(statement); @@ -11827,8 +15713,19 @@ namespace sqlite_orm { this->execute(statement); } + template<class T, class It, class L> + void replace_range(It from, It to, L transformer) { + this->assert_mapped_type<T>(); + if(from == to) { + return; + } + + auto statement = this->prepare(sqlite_orm::replace_range<T>(from, to, std::move(transformer))); + this->execute(statement); + } + template<class O, class... Cols> - int insert(const O &o, columns_t<Cols...> cols) { + int insert(const O& o, columns_t<Cols...> cols) { constexpr const size_t colsCount = std::tuple_size<std::tuple<Cols...>>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); this->assert_mapped_type<O>(); @@ -11842,22 +15739,116 @@ namespace sqlite_orm { * @return id of just created object. */ template<class O> - int insert(const O &o) { + int insert(const O& o) { this->assert_mapped_type<O>(); - auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); - return int(this->execute(statement)); + this->assert_insertable_type<O>(); + + return call_insert_impl_and_catch_constraint_failed([this, &o]() { + auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); + return int(this->execute(statement)); + }); + } + + /** + * Raw insert routine. Use this if `insert` with object does not fit you. This insert is designed to be able + * to call any type of `INSERT` query with no limitations. + * @example + * ```sql + * INSERT INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * storage.insert(into<User>, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); + * ``` + * One more example: + * ```sql + * INSERT INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * storage.insert(into<Singer>(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * INSERT INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * storage.insert(into<Singer>(), default_values()); + * ``` + * Also one can use `INSERT OR ABORT`/`INSERT OR FAIL`/`INSERT OR IGNORE`/`INSERT OR REPLACE`/`INSERT ROLLBACK`: + * ```c++ + * storage.insert(or_ignore(), into<Singer>(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); + * storage.insert(or_rollback(), into<Singer>(), default_values()); + * storage.insert(or_abort(), into<User>, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); + * ``` + */ + template<class... Args> + void insert(Args... args) { + auto statement = this->prepare(sqlite_orm::insert(std::forward<Args>(args)...)); + this->execute(statement); + } + + /** + * Raw replace statement creation routine. Use this if `replace` with object does not fit you. This replace is designed to be able + * to call any type of `REPLACE` query with no limitations. Actually this is the same query as raw insert except `OR...` option existance. + * @example + * ```sql + * REPLACE INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * storage.prepare(replace(into<User>, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * ``` + * One more example: + * ```sql + * REPLACE INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * storage.prepare(replace(into<Singer>(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * REPLACE INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * storage.prepare(replace(into<Singer>(), default_values())); + * ``` + */ + template<class... Args> + void replace(Args... args) { + auto statement = this->prepare(sqlite_orm::replace(std::forward<Args>(args)...)); + this->execute(statement); } template<class It> void insert_range(It from, It to) { using O = typename std::iterator_traits<It>::value_type; this->assert_mapped_type<O>(); + this->assert_insertable_type<O>(); if(from == to) { return; } - auto statement = this->prepare(sqlite_orm::insert_range(from, to)); - this->execute(statement); + call_insert_impl_and_catch_constraint_failed([this, from, to]() { + auto statement = this->prepare(sqlite_orm::insert_range(from, to)); + this->execute(statement); + }); + } + + template<class T, class It, class L> + void insert_range(It from, It to, L transformer) { + this->assert_mapped_type<T>(); + this->assert_insertable_type<T>(); + if(from == to) { + return; + } + call_insert_impl_and_catch_constraint_failed([this, from, to, transformer = std::move(transformer)]() { + auto statement = this->prepare(sqlite_orm::insert_range<T>(from, to, std::move(transformer))); + this->execute(statement); + }); } /** @@ -11867,7 +15858,7 @@ namespace sqlite_orm { template<class O> void rename_table(std::string name) { this->assert_mapped_type<O>(); - auto &tImpl = this->get_impl<O>(); + auto& tImpl = this->get_impl<O>(); tImpl.table.name = move(name); } @@ -11878,30 +15869,44 @@ namespace sqlite_orm { * any SQLite queries */ template<class O> - const std::string &tablename() const { + const std::string& tablename() const { this->assert_mapped_type<O>(); - auto &tImpl = this->get_impl<O>(); + auto& tImpl = this->get_impl<O>(); return tImpl.table.name; } + template<class F, class O> + const std::string* column_name(F O::*memberPointer) const { + return this->impl.column_name(memberPointer); + } + protected: template<class... Tss, class... Cols> - sync_schema_result sync_table(const storage_impl<index_t<Cols...>, Tss...> &tableImpl, sqlite3 *db, bool) { + sync_schema_result schema_status(const storage_impl<index_t<Cols...>, Tss...>&, sqlite3*, bool) { + return sync_schema_result::already_in_sync; + } + + template<class T, bool WithoutRowId, class... Cs, class... Tss> + sync_schema_result schema_status(const storage_impl<table_t<T, WithoutRowId, Cs...>, Tss...>& tImpl, + sqlite3* db, + bool preserve) { + return tImpl.schema_status(db, preserve); + } + + template<class... Tss, class... Cols> + sync_schema_result sync_table(const storage_impl<index_t<Cols...>, Tss...>& tableImpl, sqlite3* db, bool) { auto res = sync_schema_result::already_in_sync; using context_t = serializator_context<impl_type>; context_t context{this->impl}; auto query = serialize(tableImpl.table, context); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(db, query); return res; } - template<class... Tss, class... Cs> - sync_schema_result - sync_table(const storage_impl<table_t<Cs...>, Tss...> &tImpl, sqlite3 *db, bool preserve) { + template<class T, bool WithoutRowId, class... Args, class... Tss> + sync_schema_result sync_table(const storage_impl<table_t<T, WithoutRowId, Args...>, Tss...>& tImpl, + sqlite3* db, + bool preserve) { auto res = sync_schema_result::already_in_sync; auto schema_stat = tImpl.schema_status(db, preserve); @@ -11921,9 +15926,9 @@ namespace sqlite_orm { auto dbTableInfo = tImpl.get_table_info(tImpl.table.name, db); // this vector will contain pointers to columns that gotta be added.. - std::vector<table_info *> columnsToAdd; + std::vector<table_info*> columnsToAdd; - tImpl.get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + tImpl.calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); if(schema_stat == sync_schema_result::old_columns_removed) { @@ -11955,6 +15960,24 @@ namespace sqlite_orm { return res; } + template<typename S> + prepared_statement_t<S> prepare_impl(S statement) { + auto con = this->get_connection(); + sqlite3_stmt* stmt; + auto db = con.get(); + using context_t = serializator_context<impl_type>; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(statement, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return prepared_statement_t<S>{std::forward<S>(statement), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + public: /** * This is a cute function used to replace migration up/down functionality. @@ -11987,9 +16010,9 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map<std::string, sync_schema_result> result; auto db = con.get(); - this->impl.for_each([&result, db, preserve, this](auto &tableImpl) { - auto res = this->sync_table(tableImpl, db, preserve); - result.insert({tableImpl.table.name, res}); + this->impl.for_each([&result, db, preserve, this](auto& storageImpl) { + auto res = this->sync_table(storageImpl, db, preserve); + result.insert({storageImpl.table.name, res}); }); return result; } @@ -12003,8 +16026,9 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map<std::string, sync_schema_result> result; auto db = con.get(); - this->impl.for_each([&result, db, preserve](auto tableImpl) { - result.insert({tableImpl.table.name, tableImpl.schema_status(db, preserve)}); + this->impl.for_each([&result, db, preserve, this](auto& tableImpl) { + auto schemaStatus = this->schema_status(tableImpl, db, preserve); + result.insert({tableImpl.table.name, schemaStatus}); }); return result; } @@ -12014,7 +16038,7 @@ namespace sqlite_orm { * Note: table can be not mapped to a storage * @return true if table with a given name exists in db, false otherwise. */ - bool table_exists(const std::string &tableName) { + bool table_exists(const std::string& tableName) { auto con = this->get_connection(); return this->impl.table_exists(tableName, con.get()); } @@ -12022,305 +16046,147 @@ namespace sqlite_orm { template<class T, class... Args> prepared_statement_t<select_t<T, Args...>> prepare(select_t<T, Args...> sel) { sel.highest_level = true; - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(sel, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(sel), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<select_t<T, Args...>>(std::move(sel)); } template<class T, class... Args> prepared_statement_t<get_all_t<T, Args...>> prepare(get_all_t<T, Args...> get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<get_all_t<T, Args...>>(std::move(get_)); } template<class T, class... Args> prepared_statement_t<get_all_pointer_t<T, Args...>> prepare(get_all_pointer_t<T, Args...> get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<get_all_pointer_t<T, Args...>>(std::move(get_)); + } + + template<class... Args> + prepared_statement_t<replace_raw_t<Args...>> prepare(replace_raw_t<Args...> ins) { + return prepare_impl<replace_raw_t<Args...>>(std::move(ins)); + } + + template<class... Args> + prepared_statement_t<insert_raw_t<Args...>> prepare(insert_raw_t<Args...> ins) { + return prepare_impl<insert_raw_t<Args...>>(std::move(ins)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class R, class... Args> prepared_statement_t<get_all_optional_t<T, R, Args...>> prepare(get_all_optional_t<T, R, Args...> get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<get_all_optional_t<T, R, Args...>>(std::move(get_)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class... Args, class... Wargs> prepared_statement_t<update_all_t<set_t<Args...>, Wargs...>> prepare(update_all_t<set_t<Args...>, Wargs...> upd) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(upd, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(upd), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<update_all_t<set_t<Args...>, Wargs...>>(std::move(upd)); } template<class T, class... Args> prepared_statement_t<remove_all_t<T, Args...>> prepare(remove_all_t<T, Args...> rem) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rem, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rem), stmt, std::move(con)}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<remove_all_t<T, Args...>>(std::move(rem)); } template<class T, class... Ids> prepared_statement_t<get_t<T, Ids...>> prepare(get_t<T, Ids...> get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<get_t<T, Ids...>>(std::move(get_)); } template<class T, class... Ids> prepared_statement_t<get_pointer_t<T, Ids...>> prepare(get_pointer_t<T, Ids...> get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<get_pointer_t<T, Ids...>>(std::move(get_)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class... Ids> prepared_statement_t<get_optional_t<T, Ids...>> prepare(get_optional_t<T, Ids...> get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<get_optional_t<T, Ids...>>(std::move(get_)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> prepared_statement_t<update_t<T>> prepare(update_t<T> upd) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(upd, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(upd), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<update_t<T>>(std::move(upd)); } template<class T, class... Ids> prepared_statement_t<remove_t<T, Ids...>> prepare(remove_t<T, Ids...> rem) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rem, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rem), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<remove_t<T, Ids...>>(std::move(rem)); } template<class T> prepared_statement_t<insert_t<T>> prepare(insert_t<T> ins) { using object_type = typename expression_object_type<decltype(ins)>::type; this->assert_mapped_type<object_type>(); - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(ins, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(ins), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + this->assert_insertable_type<object_type>(); + return prepare_impl<insert_t<T>>(std::move(ins)); } template<class T> prepared_statement_t<replace_t<T>> prepare(replace_t<T> rep) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; using object_type = typename expression_object_type<decltype(rep)>::type; this->assert_mapped_type<object_type>(); - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rep, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rep), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl<replace_t<T>>(std::move(rep)); } - template<class It> - prepared_statement_t<insert_range_t<It>> prepare(insert_range_t<It> statement) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(statement, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(statement), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + template<class It, class L, class O> + prepared_statement_t<insert_range_t<It, L, O>> prepare(insert_range_t<It, L, O> statement) { + using object_type = typename expression_object_type<decltype(statement)>::type; + this->assert_mapped_type<object_type>(); + this->assert_insertable_type<object_type>(); + return prepare_impl<insert_range_t<It, L, O>>(std::move(statement)); } - template<class It> - prepared_statement_t<replace_range_t<It>> prepare(replace_range_t<It> rep) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rep, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rep), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + template<class It, class L, class O> + prepared_statement_t<replace_range_t<It, L, O>> prepare(replace_range_t<It, L, O> rep) { + return prepare_impl<replace_range_t<It, L, O>>(std::move(rep)); } template<class T, class... Cols> prepared_statement_t<insert_explicit<T, Cols...>> prepare(insert_explicit<T, Cols...> ins) { using object_type = typename expression_object_type<decltype(ins)>::type; this->assert_mapped_type<object_type>(); + return prepare_impl<insert_explicit<T, Cols...>>(std::move(ins)); + } + + template<class... Args> + void execute(const prepared_statement_t<replace_raw_t<Args...>>& statement) { auto con = this->get_connection(); - sqlite3_stmt *stmt; auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(ins, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(ins), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + auto stmt = statement.stmt; + sqlite3_reset(stmt); + auto index = 1; + iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { + using node_type = typename std::decay<decltype(node)>::type; + conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + perform_step(db, stmt); + } + + template<class... Args> + void execute(const prepared_statement_t<insert_raw_t<Args...>>& statement) { + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + sqlite3_reset(stmt); + auto index = 1; + iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { + using node_type = typename std::decay<decltype(node)>::type; + conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + perform_step(db, stmt); } template<class T, class... Cols> - int64 execute(const prepared_statement_t<insert_explicit<T, Cols...>> &statement) { + int64 execute(const prepared_statement_t<insert_explicit<T, Cols...>>& statement) { using statement_type = typename std::decay<decltype(statement)>::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type<expression_type>::type; @@ -12328,40 +16194,37 @@ namespace sqlite_orm { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto &tImpl = this->get_impl<object_type>(); - auto &o = statement.t.obj; + auto& tImpl = this->get_impl<object_type>(); + auto& o = statement.t.obj; sqlite3_reset(stmt); - iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto &m) { + iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto& m) { using column_type = typename std::decay<decltype(m)>::type; using field_type = typename column_result_t<self, column_type>::type; - const field_type *value = tImpl.table.template get_object_field_pointer<field_type>(o, m); + const field_type* value = tImpl.table.template get_object_field_pointer<field_type>(o, m); if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, *value)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - return sqlite3_last_insert_rowid(db); - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); + return sqlite3_last_insert_rowid(db); } - template<class It> - void execute(const prepared_statement_t<replace_range_t<It>> &statement) { + template<class T, + typename std::enable_if<(is_replace_range<T>::value || is_replace<T>::value)>::type* = nullptr> + void execute(const prepared_statement_t<T>& statement) { using statement_type = typename std::decay<decltype(statement)>::type; using expression_type = typename statement_type::expression_type; - using object_type = typename expression_type::object_type; - auto &tImpl = this->get_impl<object_type>(); + using object_type = typename expression_object_type<expression_type>::type; + auto& tImpl = this->get_impl<object_type>(); auto index = 1; auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; sqlite3_reset(stmt); - for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { - auto &o = *it; - tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + + auto processObject = [&index, &stmt, &tImpl, db](auto& o) { + tImpl.table.for_each_column([&](auto& c) { using column_type = typename std::decay<decltype(c)>::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -12380,184 +16243,126 @@ namespace sqlite_orm { } } }); - } - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } + }; - template<class It> - void execute(const prepared_statement_t<insert_range_t<It>> &statement) { - using statement_type = typename std::decay<decltype(statement)>::type; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_type::object_type; - auto index = 1; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto &tImpl = this->get_impl<object_type>(); - sqlite3_reset(stmt); - for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { - auto &o = *it; - tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { - if(!c.template has<constraints::primary_key_t<>>()) { - using column_type = typename std::decay<decltype(c)>::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != - statement_binder<field_type>().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } - }); - } - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + static_if<is_replace_range<T>{}>( + [&processObject](auto& statement) { + auto& transformer = statement.t.transformer; + std::for_each( /// + statement.t.range.first, + statement.t.range.second, + [&processObject, &transformer](auto& object) { + auto& realObject = transformer(object); + processObject(realObject); + }); + }, + [&processObject](auto& statement) { + auto& o = get_object(statement.t); + processObject(o); + })(statement); + perform_step(db, stmt); } - template<class T> - void execute(const prepared_statement_t<replace_t<T>> &statement) { + template<class T, + typename std::enable_if<(is_insert_range<T>::value || is_insert<T>::value)>::type* = nullptr> + int64 execute(const prepared_statement_t<T>& statement) { using statement_type = typename std::decay<decltype(statement)>::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type<expression_type>::type; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; auto index = 1; - auto &o = get_object(statement.t); - auto &tImpl = this->get_impl<object_type>(); - sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { - using column_type = typename std::decay<decltype(c)>::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - - template<class T> - int64 execute(const prepared_statement_t<insert_t<T>> &statement) { - using statement_type = typename std::decay<decltype(statement)>::type; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type<expression_type>::type; - int64 res = 0; auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto index = 1; - auto &tImpl = this->get_impl<object_type>(); - auto &o = get_object(statement.t); + auto& tImpl = this->get_impl<object_type>(); auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, &index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto &c) { - if(tImpl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()) { - auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - using column_type = typename std::decay<decltype(c)>::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != - statement_binder<field_type>().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + auto processObject = [&index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto& o) { + tImpl.table.for_each_column([&](auto& c) { + using table_type = typename std::decay<decltype(tImpl.table)>::type; + if(table_type::is_without_rowid || !c.template has<primary_key_t<>>()) { + auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + if(it == compositeKeyColumnNames.end()) { + using column_type = typename std::decay<decltype(c)>::type; + using field_type = typename column_type::field_type; + if(c.member_pointer) { + if(SQLITE_OK != + statement_binder<field_type>().bind(stmt, index++, o.*c.member_pointer)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + using getter_type = typename column_type::getter_type; + field_value_holder<getter_type> valueHolder{((o).*(c.getter))()}; + if(SQLITE_OK != + statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } } } } - } - }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - res = sqlite3_last_insert_rowid(db); - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - return res; + }); + }; + + static_if<is_insert_range<T>{}>( + [&processObject](auto& statement) { + auto& transformer = statement.t.transformer; + std::for_each( /// + statement.t.range.first, + statement.t.range.second, + [&processObject, &transformer](auto& object) { + auto& realObject = transformer(object); + processObject(realObject); + }); + }, + [&processObject](auto& statement) { + auto& o = get_object(statement.t); + processObject(o); + })(statement); + + perform_step(db, stmt); + return sqlite3_last_insert_rowid(db); } template<class T, class... Ids> - void execute(const prepared_statement_t<remove_t<T, Ids...>> &statement) { + void execute(const prepared_statement_t<remove_t<T, Ids...>>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay<decltype(v)>::type; if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template<class T> - void execute(const prepared_statement_t<update_t<T>> &statement) { + void execute(const prepared_statement_t<update_t<T>>& statement) { using statement_type = typename std::decay<decltype(statement)>::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type<expression_type>::type; auto con = this->get_connection(); auto db = con.get(); - auto &tImpl = this->get_impl<object_type>(); + auto& tImpl = this->get_impl<object_type>(); auto stmt = statement.stmt; auto index = 1; - auto &o = get_object(statement.t); + auto& o = get_object(statement.t); sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { - if(!c.template has<constraints::primary_key_t<>>()) { - using column_type = typename std::decay<decltype(c)>::type; + tImpl.table.for_each_column([&o, stmt, &index, db, &tImpl](auto& column) { + if(!column.template has<primary_key_t<>>() && + !tImpl.table.exists_in_composite_primary_key(column)) { + using column_type = typename std::decay<decltype(column)>::type; using field_type = typename column_type::field_type; - if(c.member_pointer) { - auto bind_res = statement_binder<field_type>().bind(stmt, index++, o.*c.member_pointer); + if(column.member_pointer) { + auto bind_res = + statement_binder<field_type>().bind(stmt, index++, o.*column.member_pointer); if(SQLITE_OK != bind_res) { throw std::system_error( std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -12565,7 +16370,7 @@ namespace sqlite_orm { } } else { using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(c.getter))()}; + field_value_holder<getter_type> valueHolder{((o).*(column.getter))()}; if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { throw std::system_error( std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -12574,19 +16379,20 @@ namespace sqlite_orm { } } }); - tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { - if(c.template has<constraints::primary_key_t<>>()) { - using column_type = typename std::decay<decltype(c)>::type; + tImpl.table.for_each_column([&o, stmt, &index, db, &tImpl](auto& column) { + if(column.template has<primary_key_t<>>() || tImpl.table.exists_in_composite_primary_key(column)) { + using column_type = typename std::decay<decltype(column)>::type; using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, o.*c.member_pointer)) { + if(column.member_pointer) { + if(SQLITE_OK != + statement_binder<field_type>().bind(stmt, index++, o.*column.member_pointer)) { throw std::system_error( std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } else { using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(c.getter))()}; + field_value_holder<getter_type> valueHolder{((o).*(column.getter))()}; if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { throw std::system_error( std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -12595,23 +16401,18 @@ namespace sqlite_orm { } } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template<class T, class... Ids> - std::unique_ptr<T> execute(const prepared_statement_t<get_pointer_t<T, Ids...>> &statement) { - auto &tImpl = this->get_impl<T>(); + std::unique_ptr<T> execute(const prepared_statement_t<get_pointer_t<T, Ids...>>& statement) { + auto& tImpl = this->get_impl<T>(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay<decltype(v)>::type; if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -12638,14 +16439,14 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class... Ids> - std::optional<T> execute(const prepared_statement_t<get_optional_t<T, Ids...>> &statement) { - auto &tImpl = this->get_impl<T>(); + std::optional<T> execute(const prepared_statement_t<get_optional_t<T, Ids...>>& statement) { + auto& tImpl = this->get_impl<T>(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay<decltype(v)>::type; if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -12672,14 +16473,14 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class... Ids> - T execute(const prepared_statement_t<get_t<T, Ids...>> &statement) { - auto &tImpl = this->get_impl<T>(); + T execute(const prepared_statement_t<get_t<T, Ids...>>& statement) { + auto& tImpl = this->get_impl<T>(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay<decltype(v)>::type; if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -12705,13 +16506,13 @@ namespace sqlite_orm { } template<class T, class... Args> - void execute(const prepared_statement_t<remove_all_t<T, Args...>> &statement) { + void execute(const prepared_statement_t<remove_all_t<T, Args...>>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto &node) { + iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { using node_type = typename std::decay<decltype(node)>::type; conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12719,23 +16520,18 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template<class... Args, class... Wargs> - void execute(const prepared_statement_t<update_all_t<set_t<Args...>, Wargs...>> &statement) { + void execute(const prepared_statement_t<update_all_t<set_t<Args...>, Wargs...>>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_tuple(statement.t.set.assigns, [&index, stmt, db](auto &setArg) { - iterate_ast(setArg, [&index, stmt, db](auto &node) { + iterate_tuple(statement.t.set.assigns, [&index, stmt, db](auto& setArg) { + iterate_ast(setArg, [&index, stmt, db](auto& node) { using node_type = typename std::decay<decltype(node)>::type; conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12744,7 +16540,7 @@ namespace sqlite_orm { } }); }); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto &node) { + iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { using node_type = typename std::decay<decltype(node)>::type; conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12752,22 +16548,17 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template<class T, class... Args, class R = typename column_result_t<self, T>::type> - std::vector<R> execute(const prepared_statement_t<select_t<T, Args...>> &statement) { + std::vector<R> execute(const prepared_statement_t<select_t<T, Args...>>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay<decltype(node)>::type; conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12801,14 +16592,14 @@ namespace sqlite_orm { } template<class T, class R, class... Args> - R execute(const prepared_statement_t<get_all_t<T, R, Args...>> &statement) { - auto &tImpl = this->get_impl<T>(); + R execute(const prepared_statement_t<get_all_t<T, R, Args...>>& statement) { + auto& tImpl = this->get_impl<T>(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay<decltype(node)>::type; conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12839,14 +16630,14 @@ namespace sqlite_orm { } template<class T, class R, class... Args> - R execute(const prepared_statement_t<get_all_pointer_t<T, R, Args...>> &statement) { - auto &tImpl = this->get_impl<T>(); + R execute(const prepared_statement_t<get_all_pointer_t<T, R, Args...>>& statement) { + auto& tImpl = this->get_impl<T>(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay<decltype(node)>::type; conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12878,14 +16669,14 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class R, class... Args> - R execute(const prepared_statement_t<get_all_optional_t<T, R, Args...>> &statement) { - auto &tImpl = this->get_impl<T>(); + R execute(const prepared_statement_t<get_all_optional_t<T, R, Args...>>& statement) { + auto& tImpl = this->get_impl<T>(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay<decltype(node)>::type; conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12915,6 +16706,88 @@ namespace sqlite_orm { return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template<class O> + bool has_dependent_rows(const O& object) { + auto res = false; + this->impl.for_each([this, &object, &res](auto& storageImpl) { + if(res) { + return; + } + storageImpl.table.for_each_foreign_key([&storageImpl, this, &object, &res](auto& foreignKey) { + using ForeignKey = typename std::decay<decltype(foreignKey)>::type; + using TargetType = typename ForeignKey::target_type; + if(std::is_same<TargetType, O>::value) { + std::stringstream ss; + ss << "SELECT COUNT(*)"; + ss << " FROM " << storageImpl.table.name; + ss << " WHERE"; + auto columnIndex = 0; + iterate_tuple(foreignKey.columns, [&ss, &columnIndex, &storageImpl](auto& column) { + if(columnIndex > 0) { + ss << " AND"; + } + if(auto columnName = storageImpl.table.find_column_name(column)) { + ss << ' ' << *columnName << " = ?"; + } else { + throw std::system_error( + std::make_error_code(sqlite_orm::orm_error_code::column_not_found)); + } + ++columnIndex; + }); + auto query = ss.str(); + ss.flush(); + auto con = this->get_connection(); + sqlite3_stmt* stmt; + auto db = con.get(); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer(stmt); + columnIndex = 1; + iterate_tuple( + foreignKey.references, + [&columnIndex, stmt, &object, db](auto& memberPointer) { + using MemberPointer = typename std::decay<decltype(memberPointer)>::type; + using field_type = typename member_traits<MemberPointer>::field_type; + // if(column.member_pointer) { + if(SQLITE_OK != statement_binder<field_type>().bind(stmt, + columnIndex++, + object.*memberPointer)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + /*} else { + using getter_type = typename column_type::getter_type; + field_value_holder<getter_type> valueHolder{((object).*(column.getter))()}; + if(SQLITE_OK != statement_binder<field_type>().bind(stmt, columnIndex++, valueHolder.value)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }*/ + }); + if(SQLITE_ROW != sqlite3_step(stmt)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + auto countResult = sqlite3_column_int(stmt, 0); + res = countResult > 0; + if(SQLITE_DONE != sqlite3_step(stmt)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + }); + }); + return res; + } }; // struct storage_t template<class T> @@ -12925,8 +16798,8 @@ namespace sqlite_orm { } template<class... Ts> - internal::storage_t<Ts...> make_storage(const std::string &filename, Ts... tables) { - return {filename, internal::storage_impl<Ts...>(tables...)}; + internal::storage_t<Ts...> make_storage(const std::string& filename, Ts... tables) { + return {filename, internal::storage_impl<Ts...>(std::forward<Ts>(tables)...)}; } /** @@ -12953,6 +16826,9 @@ __pragma(pop_macro("min")) #include <tuple> // std::tuple #include <utility> // std::pair #include <functional> // std::reference_wrapper +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include <optional> // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED // #include "conditions.h" @@ -12966,6 +16842,14 @@ __pragma(pop_macro("min")) // #include "core_functions.h" + // #include "function.h" + + // #include "ast/excluded.h" + + // #include "ast/upsert_clause.h" + + // #include "ast/where.h" + namespace sqlite_orm { namespace internal { @@ -12979,12 +16863,32 @@ __pragma(pop_macro("min")) struct node_tuple<void, void> { using type = std::tuple<>; }; - +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<class T> + struct node_tuple<as_optional_t<T>, void> { + using type = typename node_tuple<T>::type; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> struct node_tuple<std::reference_wrapper<T>, void> { using type = typename node_tuple<T>::type; }; + template<class... TargetArgs, class... ActionsArgs> + struct node_tuple<upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>, void> { + using type = typename node_tuple<std::tuple<ActionsArgs...>>::type; + }; + + template<class... Args> + struct node_tuple<set_t<Args...>, void> { + using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + }; + + template<class T> + struct node_tuple<excluded_t<T>, void> { + using type = typename node_tuple<T>::type; + }; + template<class C> struct node_tuple<where_t<C>, void> { using node_type = where_t<C>; @@ -13018,13 +16922,21 @@ __pragma(pop_macro("min")) }; template<class L, class A> - struct node_tuple<in_t<L, A>, void> { - using node_type = in_t<L, A>; + struct node_tuple<dynamic_in_t<L, A>, void> { + using node_type = dynamic_in_t<L, A>; using left_tuple = typename node_tuple<L>::type; using right_tuple = typename node_tuple<A>::type; using type = typename conc_tuple<left_tuple, right_tuple>::type; }; + template<class L, class... Args> + struct node_tuple<in_t<L, Args...>, void> { + using node_type = in_t<L, Args...>; + using left_tuple = typename node_tuple<L>::type; + using right_tuple = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = typename conc_tuple<left_tuple, right_tuple>::type; + }; + template<class T> struct node_tuple<T, typename std::enable_if<is_base_of_template<T, compound_operator>::value>::type> { using node_type = T; @@ -13043,6 +16955,36 @@ __pragma(pop_macro("min")) using type = typename conc_tuple<columns_tuple, args_tuple>::type; }; + template<class... Args> + struct node_tuple<insert_raw_t<Args...>, void> { + using node_type = insert_raw_t<Args...>; + using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + }; + + template<class... Args> + struct node_tuple<replace_raw_t<Args...>, void> { + using node_type = replace_raw_t<Args...>; + using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + }; + + template<class T> + struct node_tuple<into_t<T>, void> { + using node_type = into_t<T>; + using type = std::tuple<>; + }; + + template<class... Args> + struct node_tuple<values_t<Args...>, void> { + using node_type = values_t<Args...>; + using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + }; + + template<class... Args> + struct node_tuple<std::tuple<Args...>, void> { + using node_type = std::tuple<Args...>; + using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + }; + template<class T, class R, class... Args> struct node_tuple<get_all_t<T, R, Args...>, void> { using node_type = get_all_t<T, R, Args...>; @@ -13158,8 +17100,14 @@ __pragma(pop_macro("min")) }; template<class R, class S, class... Args> - struct node_tuple<core_function_t<R, S, Args...>, void> { - using node_type = core_function_t<R, S, Args...>; + struct node_tuple<built_in_function_t<R, S, Args...>, void> { + using node_type = built_in_function_t<R, S, Args...>; + using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + }; + + template<class F, class... Args> + struct node_tuple<function_call<F, Args...>, void> { + using node_type = function_call<F, Args...>; using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; }; @@ -13249,132 +17197,132 @@ __pragma(pop_macro("min")) namespace sqlite_orm { - template<int N, class It> - auto &get(internal::prepared_statement_t<internal::insert_range_t<It>> &statement) { + template<int N, class It, class L, class O> + auto& get(internal::prepared_statement_t<internal::insert_range_t<It, L, O>>& statement) { return std::get<N>(statement.t.range); } - template<int N, class It> - const auto &get(const internal::prepared_statement_t<internal::insert_range_t<It>> &statement) { + template<int N, class It, class L, class O> + const auto& get(const internal::prepared_statement_t<internal::insert_range_t<It, L, O>>& statement) { return std::get<N>(statement.t.range); } - template<int N, class It> - auto &get(internal::prepared_statement_t<internal::replace_range_t<It>> &statement) { + template<int N, class It, class L, class O> + auto& get(internal::prepared_statement_t<internal::replace_range_t<It, L, O>>& statement) { return std::get<N>(statement.t.range); } - template<int N, class It> - const auto &get(const internal::prepared_statement_t<internal::replace_range_t<It>> &statement) { + template<int N, class It, class L, class O> + const auto& get(const internal::prepared_statement_t<internal::replace_range_t<It, L, O>>& statement) { return std::get<N>(statement.t.range); } template<int N, class T, class... Ids> - auto &get(internal::prepared_statement_t<internal::get_t<T, Ids...>> &statement) { + auto& get(internal::prepared_statement_t<internal::get_t<T, Ids...>>& statement) { return internal::get_ref(std::get<N>(statement.t.ids)); } template<int N, class T, class... Ids> - const auto &get(const internal::prepared_statement_t<internal::get_t<T, Ids...>> &statement) { + const auto& get(const internal::prepared_statement_t<internal::get_t<T, Ids...>>& statement) { return internal::get_ref(std::get<N>(statement.t.ids)); } template<int N, class T, class... Ids> - auto &get(internal::prepared_statement_t<internal::get_pointer_t<T, Ids...>> &statement) { + auto& get(internal::prepared_statement_t<internal::get_pointer_t<T, Ids...>>& statement) { return internal::get_ref(std::get<N>(statement.t.ids)); } template<int N, class T, class... Ids> - const auto &get(const internal::prepared_statement_t<internal::get_pointer_t<T, Ids...>> &statement) { + const auto& get(const internal::prepared_statement_t<internal::get_pointer_t<T, Ids...>>& statement) { return internal::get_ref(std::get<N>(statement.t.ids)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<int N, class T, class... Ids> - auto &get(internal::prepared_statement_t<internal::get_optional_t<T, Ids...>> &statement) { + auto& get(internal::prepared_statement_t<internal::get_optional_t<T, Ids...>>& statement) { return internal::get_ref(std::get<N>(statement.t.ids)); } template<int N, class T, class... Ids> - const auto &get(const internal::prepared_statement_t<internal::get_optional_t<T, Ids...>> &statement) { + const auto& get(const internal::prepared_statement_t<internal::get_optional_t<T, Ids...>>& statement) { return internal::get_ref(std::get<N>(statement.t.ids)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<int N, class T, class... Ids> - auto &get(internal::prepared_statement_t<internal::remove_t<T, Ids...>> &statement) { + auto& get(internal::prepared_statement_t<internal::remove_t<T, Ids...>>& statement) { return internal::get_ref(std::get<N>(statement.t.ids)); } template<int N, class T, class... Ids> - const auto &get(const internal::prepared_statement_t<internal::remove_t<T, Ids...>> &statement) { + const auto& get(const internal::prepared_statement_t<internal::remove_t<T, Ids...>>& statement) { return internal::get_ref(std::get<N>(statement.t.ids)); } template<int N, class T> - auto &get(internal::prepared_statement_t<internal::update_t<T>> &statement) { + auto& get(internal::prepared_statement_t<internal::update_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); return internal::get_ref(statement.t.obj); } template<int N, class T> - const auto &get(const internal::prepared_statement_t<internal::update_t<T>> &statement) { + const auto& get(const internal::prepared_statement_t<internal::update_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); return internal::get_ref(statement.t.obj); } template<int N, class T, class... Cols> - auto &get(internal::prepared_statement_t<internal::insert_explicit<T, Cols...>> &statement) { + auto& get(internal::prepared_statement_t<internal::insert_explicit<T, Cols...>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template<int N, class T, class... Cols> - const auto &get(const internal::prepared_statement_t<internal::insert_explicit<T, Cols...>> &statement) { + const auto& get(const internal::prepared_statement_t<internal::insert_explicit<T, Cols...>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template<int N, class T> - auto &get(internal::prepared_statement_t<internal::replace_t<T>> &statement) { + auto& get(internal::prepared_statement_t<internal::replace_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); return internal::get_ref(statement.t.obj); } template<int N, class T> - const auto &get(const internal::prepared_statement_t<internal::replace_t<T>> &statement) { + const auto& get(const internal::prepared_statement_t<internal::replace_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); return internal::get_ref(statement.t.obj); } template<int N, class T> - auto &get(internal::prepared_statement_t<internal::insert_t<T>> &statement) { + auto& get(internal::prepared_statement_t<internal::insert_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template<int N, class T> - const auto &get(const internal::prepared_statement_t<internal::insert_t<T>> &statement) { + const auto& get(const internal::prepared_statement_t<internal::insert_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template<int N, class T> - const auto &get(const internal::prepared_statement_t<T> &statement) { + const auto& get(const internal::prepared_statement_t<T>& statement) { using statement_type = typename std::decay<decltype(statement)>::type; using expression_type = typename statement_type::expression_type; using node_tuple = typename internal::node_tuple<expression_type>::type; using bind_tuple = typename internal::bindable_filter<node_tuple>::type; - using result_tupe = typename std::tuple_element<N, bind_tuple>::type; - const result_tupe *result = nullptr; + using result_tupe = typename std::tuple_element<static_cast<size_t>(N), bind_tuple>::type; + const result_tupe* result = nullptr; auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto &node) { + internal::iterate_ast(statement.t, [&result, &index](auto& node) { using node_type = typename std::decay<decltype(node)>::type; if(internal::is_bindable<node_type>::value) { ++index; } if(index == N) { - internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto &r, auto &n) { + internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto& r, auto& n) { r = const_cast<typename std::remove_reference<decltype(r)>::type>(&n); })(result, node); } @@ -13383,21 +17331,21 @@ namespace sqlite_orm { } template<int N, class T> - auto &get(internal::prepared_statement_t<T> &statement) { + auto& get(internal::prepared_statement_t<T>& statement) { using statement_type = typename std::decay<decltype(statement)>::type; using expression_type = typename statement_type::expression_type; using node_tuple = typename internal::node_tuple<expression_type>::type; using bind_tuple = typename internal::bindable_filter<node_tuple>::type; - using result_tupe = typename std::tuple_element<N, bind_tuple>::type; - result_tupe *result = nullptr; + using result_tupe = typename std::tuple_element<static_cast<size_t>(N), bind_tuple>::type; + result_tupe* result = nullptr; auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto &node) { + internal::iterate_ast(statement.t, [&result, &index](auto& node) { using node_type = typename std::decay<decltype(node)>::type; if(internal::is_bindable<node_type>::value) { ++index; } if(index == N) { - internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto &r, auto &n) { + internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto& r, auto& n) { r = const_cast<typename std::remove_reference<decltype(r)>::type>(&n); })(result, node); } diff --git a/run_prepared_release_test.sh b/run_prepared_release_test.sh new file mode 100644 index 00000000..65bad643 --- /dev/null +++ b/run_prepared_release_test.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cd ./_PREPARED_RELEASE_TEST +./SSVOpenHexagon.exe diff --git a/src/SSVOpenHexagon/Components/CPlayer.cpp b/src/SSVOpenHexagon/Components/CPlayer.cpp index 27bcb239..dc112760 100644 --- a/src/SSVOpenHexagon/Components/CPlayer.cpp +++ b/src/SSVOpenHexagon/Components/CPlayer.cpp @@ -3,21 +3,27 @@ // AFL License page: https://opensource.org/licenses/AFL-3.0 #include "SSVOpenHexagon/Components/CPlayer.hpp" -#include "SSVOpenHexagon/Components/CWall.hpp" + #include "SSVOpenHexagon/Components/CCustomWall.hpp" +#include "SSVOpenHexagon/Components/CWall.hpp" + #include "SSVOpenHexagon/Utils/Color.hpp" -#include "SSVOpenHexagon/Utils/Ticker.hpp" -#include "SSVOpenHexagon/Utils/PointInPolygon.hpp" -#include "SSVOpenHexagon/Utils/Geometry.hpp" #include "SSVOpenHexagon/Utils/Easing.hpp" +#include "SSVOpenHexagon/Utils/Geometry.hpp" +#include "SSVOpenHexagon/Utils/MoveTowards.hpp" +#include "SSVOpenHexagon/Utils/PointInPolygon.hpp" +#include "SSVOpenHexagon/Utils/Ticker.hpp" #include <SSVStart/Utils/SFML.hpp> #include <SSVUtils/Core/Common/Frametime.hpp> #include <SSVUtils/Core/Utils/Math.hpp> -#include <SFML/System/Vector2.hpp> #include <SFML/Graphics/Color.hpp> +#include <SFML/System/Vector2.hpp> + +#include <algorithm> +#include <cmath> namespace hg { @@ -48,14 +54,29 @@ CPlayer::CPlayer(const sf::Vector2f& pos, const float swapCooldown, _triangleWidth{unfocusedTriangleWidth}, _triangleWidthTransitionTime{0.f}, _swapTimer{swapCooldown}, - _swapBlinkTimer{swapCooldown / 6.f}, - _deadEffectTimer{80.f, false} + _swapBlinkTimer{6.f}, + _deadEffectTimer{80.f, false}, + _currTiltedAngle{0} {} +[[nodiscard]] sf::Color CPlayer::getColorAdjustedForSwap( + const sf::Color& colorPlayer) const +{ + if(!_swapTimer.isRunning() && !_dead) + { + return Utils::getColorFromHue( + std::fmod(_swapBlinkTimer.getCurrent() / 12.f, 0.2f)); + } + + return !_deadEffectTimer.isRunning() ? colorPlayer + : Utils::getColorFromHue(_hue / 360.f); +} + void CPlayer::draw(const unsigned int sides, const sf::Color& colorMain, const sf::Color& colorPlayer, Utils::FastVertexVectorQuads& wallQuads, Utils::FastVertexVectorTris& capTris, - Utils::FastVertexVectorTris& playerTris, const sf::Color& capColor) + Utils::FastVertexVectorTris& playerTris, const sf::Color& capColor, + const float angleTiltIntensity) { drawPivot(sides, colorMain, wallQuads, capTris, capColor); @@ -64,25 +85,18 @@ void CPlayer::draw(const unsigned int sides, const sf::Color& colorMain, drawDeathEffect(wallQuads); } - sf::Color adjustedColorMain{!_deadEffectTimer.isRunning() - ? colorPlayer - : Utils::getColorFromHue(_hue / 360.f)}; + const float tiltedAngle = + _angle + (_currTiltedAngle * ssvu::toRad(24.f) * angleTiltIntensity); const sf::Vector2f pLeft = ssvs::getOrbitRad( - _pos, _angle - ssvu::toRad(100.f), _size + _triangleWidth); + _pos, tiltedAngle - ssvu::toRad(100.f), _size + _triangleWidth); const sf::Vector2f pRight = ssvs::getOrbitRad( - _pos, _angle + ssvu::toRad(100.f), _size + _triangleWidth); - - if(!_swapTimer.isRunning() && !_dead) - { - adjustedColorMain = - Utils::getColorFromHue((_swapBlinkTimer.getCurrent() * 15) / 360.f); - } + _pos, tiltedAngle + ssvu::toRad(100.f), _size + _triangleWidth); playerTris.reserve_more(3); - playerTris.batch_unsafe_emplace_back(adjustedColorMain, - ssvs::getOrbitRad(_pos, _angle, _size), pLeft, pRight); + playerTris.batch_unsafe_emplace_back(getColorAdjustedForSwap(colorPlayer), + ssvs::getOrbitRad(_pos, tiltedAngle, _size), pLeft, pRight); } void CPlayer::drawPivot(const unsigned int sides, const sf::Color& colorMain, @@ -388,19 +402,11 @@ void CPlayer::updateTriangleWidthTransition( { if(focused && _triangleWidthTransitionTime < 1.f) { - _triangleWidthTransitionTime += ft * 0.1f; - if(_triangleWidthTransitionTime > 1.f) - { - _triangleWidthTransitionTime = 1.f; - } + Utils::moveTowards(_triangleWidthTransitionTime, 1.f, ft * 0.1f); } else if(!focused && _triangleWidthTransitionTime > 0.f) { - _triangleWidthTransitionTime -= ft * 0.1f; - if(_triangleWidthTransitionTime < 0.f) - { - _triangleWidthTransitionTime = 0.f; - } + Utils::moveTowardsZero(_triangleWidthTransitionTime, ft * 0.1f); } _triangleWidth = @@ -436,7 +442,7 @@ void CPlayer::update( } } - _swapBlinkTimer.update(ft); + _swapBlinkTimer.update(ft / 3.f); if(swapEnabled && _swapTimer.update(ft)) { @@ -452,12 +458,19 @@ void CPlayer::updateInputMovement(const float movementDir, { _currentSpeed = playerSpeedMult * (focused ? _focusSpeed : _speed) * ft; _angle += ssvu::toRad(_currentSpeed * movementDir); + + const float inc = ft / 10.f; + + _currTiltedAngle = + (movementDir == 0.f) + ? Utils::getMoveTowardsZero(_currTiltedAngle, inc) + : Utils::getMoveTowards(_currTiltedAngle, movementDir, inc * 2.f); } void CPlayer::resetSwap(const float swapCooldown) { _swapTimer.restart(swapCooldown); - _swapBlinkTimer.restart(swapCooldown / 6.f); + _swapBlinkTimer.restart(6.f); } void CPlayer::setJustSwapped(const bool value) diff --git a/src/SSVOpenHexagon/Core/HGGraphics.cpp b/src/SSVOpenHexagon/Core/HGGraphics.cpp index 95abd3a2..ae1e085d 100644 --- a/src/SSVOpenHexagon/Core/HGGraphics.cpp +++ b/src/SSVOpenHexagon/Core/HGGraphics.cpp @@ -114,7 +114,8 @@ void HexagonGame::draw() if(status.started) { player.draw(getSides(), getColorMain(), getColorPlayer(), pivotQuads, - capTris, playerTris, getColorCap()); + capTris, playerTris, getColorCap(), + Config::getAngleTiltIntensity()); } if(Config::get3D()) @@ -228,6 +229,12 @@ void HexagonGame::draw() render(wallQuads3D); render(pivotQuads3D); render(playerTris3D); + + if(Config::getShowPlayerTrail() && status.showPlayerTrail) + { + drawTrailParticles(); + } + render(wallQuads); render(capTris); render(pivotQuads); @@ -356,6 +363,14 @@ void HexagonGame::drawParticles() } } +void HexagonGame::drawTrailParticles() +{ + for(TrailParticle& p : trailParticles) + { + render(p.sprite); + } +} + void HexagonGame::updateText(ssvu::FT mFT) { if(window == nullptr) diff --git a/src/SSVOpenHexagon/Core/HGUpdate.cpp b/src/SSVOpenHexagon/Core/HGUpdate.cpp index afe5eb47..bf742ffb 100644 --- a/src/SSVOpenHexagon/Core/HGUpdate.cpp +++ b/src/SSVOpenHexagon/Core/HGUpdate.cpp @@ -5,20 +5,24 @@ #include "SSVOpenHexagon/Core/HexagonGame.hpp" #include "SSVOpenHexagon/Components/CWall.hpp" + #include "SSVOpenHexagon/Global/Assert.hpp" #include "SSVOpenHexagon/Global/Assets.hpp" -#include "SSVOpenHexagon/Global/Config.hpp" #include "SSVOpenHexagon/Global/Audio.hpp" +#include "SSVOpenHexagon/Global/Config.hpp" + #include "SSVOpenHexagon/Utils/Concat.hpp" #include "SSVOpenHexagon/Utils/Easing.hpp" #include "SSVOpenHexagon/Utils/LevelValidator.hpp" +#include "SSVOpenHexagon/Utils/MoveTowards.hpp" #include "SSVOpenHexagon/Utils/Split.hpp" #include "SSVOpenHexagon/Utils/String.hpp" -#include "SSVOpenHexagon/Core/HexagonClient.hpp" -#include "SSVOpenHexagon/Core/Steam.hpp" + #include "SSVOpenHexagon/Core/Discord.hpp" +#include "SSVOpenHexagon/Core/HexagonClient.hpp" #include "SSVOpenHexagon/Core/Joystick.hpp" #include "SSVOpenHexagon/Core/LuaScripting.hpp" +#include "SSVOpenHexagon/Core/Steam.hpp" #include <imgui.h> #include <misc/cpp/imgui_stdlib.h> @@ -29,6 +33,10 @@ #include <SSVUtils/Core/Common/Frametime.hpp> #include <SSVUtils/Core/Utils/Containers.hpp> +#include <SFML/Config.hpp> +#include <SFML/Graphics/Color.hpp> +#include <SFML/System/Vector2.hpp> + #include <array> #include <cstring> #include <optional> @@ -333,6 +341,12 @@ void HexagonGame::update(ssvu::FT mFT, const float timescale) SSVOH_ASSERT(backgroundCamera.has_value()); updateParticles(mFT); + + if(Config::getShowPlayerTrail() && status.showPlayerTrail) + { + updateTrailParticles(mFT); + } + overlayCamera->update(mFT); backgroundCamera->update(mFT); } @@ -875,7 +889,8 @@ void HexagonGame::updateParticles(ssvu::FT mFT) { Particle p; - p.sprite.setTexture(assets.getTextureOrNullTexture("starParticle.png")); + SSVOH_ASSERT(txStarParticle != nullptr); + p.sprite.setTexture(*txStarParticle); p.sprite.setPosition( {ssvu::getRndR(-64.f, Config::getWidth() + 64.f), -64.f}); p.sprite.setRotation(ssvu::getRndR(0.f, 360.f)); @@ -913,6 +928,61 @@ void HexagonGame::updateParticles(ssvu::FT mFT) } } +void HexagonGame::updateTrailParticles(ssvu::FT mFT) +{ + SSVOH_ASSERT(window != nullptr); + + const auto isDead = [&](const TrailParticle& p) + { return p.sprite.getColor().a <= 3; }; + + const auto makeTrailParticle = [this] + { + TrailParticle p; + + SSVOH_ASSERT(txSmallCircle != nullptr); + p.sprite.setTexture(*txSmallCircle); + p.sprite.setPosition(player.getPosition()); + p.sprite.setOrigin(sf::Vector2f{txSmallCircle->getSize()} / 2.f); + + const float scale = Config::getPlayerTrailScale(); + p.sprite.setScale({scale, scale}); + + sf::Color c = Config::getPlayerTrailHasSwapColor() + ? player.getColorAdjustedForSwap(getColorPlayer()) + : getColorPlayer(); + + c.a = Config::getPlayerTrailAlpha(); + p.sprite.setColor(c); + + p.angle = player.getPlayerAngle(); + + return p; + }; + + ssvu::eraseRemoveIf(trailParticles, isDead); + + for(TrailParticle& p : trailParticles) + { + sf::Color color = p.sprite.getColor(); + + const float newAlpha = Utils::getMoveTowardsZero( + static_cast<float>(color.a), Config::getPlayerTrailDecay() * mFT); + + color.a = static_cast<sf::Uint8>(newAlpha); + p.sprite.setColor(color); + + p.sprite.setScale(p.sprite.getScale() * 0.98f); + + p.sprite.setPosition( + ssvs::getVecFromRad(p.angle, status.radius + 2.4f)); + } + + if(player.hasChangedAngle()) + { + trailParticles.emplace_back(makeTrailParticle()); + } +} + static int ilcTextEditCallbackStub(ImGuiInputTextCallbackData* data) { auto hg = (HexagonGame*)data->UserData; diff --git a/src/SSVOpenHexagon/Core/HexagonGame.cpp b/src/SSVOpenHexagon/Core/HexagonGame.cpp index d70f3a11..f6d99a90 100644 --- a/src/SSVOpenHexagon/Core/HexagonGame.cpp +++ b/src/SSVOpenHexagon/Core/HexagonGame.cpp @@ -194,7 +194,7 @@ void HexagonGame::updateLevelInfo() return s; }; - levelInfoTextLevel.setFillColor(styleData.getMainColor()); + levelInfoTextLevel.setFillColor(getColorText()); levelInfoTextLevel.setCharacterSize(20.f / Config::getZoomFactor()); levelInfoTextLevel.setString(trim(Utils::toUppercase(levelData->name))); levelInfoTextLevel.setOrigin(ssvs::getLocalNW(levelInfoTextLevel)); @@ -204,7 +204,7 @@ void HexagonGame::updateLevelInfo() const auto prepareText = [&](sf::Text& text, const float characterSize, const std::string& string) { - text.setFillColor(styleData.getTextColor()); + text.setFillColor(getColorText()); text.setCharacterSize(characterSize / Config::getZoomFactor()); text.setString(string); }; @@ -287,6 +287,8 @@ HexagonGame::HexagonGame(Steam::steam_manager* mSteamManager, levelStatus{Config::getMusicSpeedDMSync(), Config::getSpawnDistance()}, messageText{initText("", font, 38.f)}, pbText{initText("", fontBold, 65.f)}, + txStarParticle{nullptr}, + txSmallCircle{nullptr}, levelInfoTextLevel{"", font}, levelInfoTextPack{"", font}, levelInfoTextAuthor{"", font}, @@ -311,6 +313,9 @@ HexagonGame::HexagonGame(Steam::steam_manager* mSteamManager, overlayCamera.emplace( *window, sf::View{sf::Vector2f{width / 2.f, height / 2.f}, sf::Vector2f{width, height}}); + + txStarParticle = &assets.getTextureOrNullTexture("starParticle.png"); + txSmallCircle = &assets.getTextureOrNullTexture("smallCircle.png"); } @@ -665,6 +670,7 @@ void HexagonGame::newGame(const std::string& mPackId, const std::string& mId, mustSpawnPBParticles = false; nextPBParticleSpawn = 0.f; particles.clear(); + trailParticles.clear(); if(window != nullptr) { diff --git a/src/SSVOpenHexagon/Core/LuaScripting.cpp b/src/SSVOpenHexagon/Core/LuaScripting.cpp index b6d32580..7208dcfa 100644 --- a/src/SSVOpenHexagon/Core/LuaScripting.cpp +++ b/src/SSVOpenHexagon/Core/LuaScripting.cpp @@ -922,6 +922,12 @@ static void initLevelControl( "`0` and `l_getBeatPulseDelayMax()` unless manually overridden.", "Sets the current beat pulse delay value to `$0`."); + + sVar("ShowPlayerTrail", &HexagonGameStatus::showPlayerTrail, + "Gets whether the current level allows player trails to be shown.", + + "Sets whether the current level allows player trails to be shown to " + "`$0`."); } static void initStyleControl(Lua::LuaContext& lua, StyleData& styleData) diff --git a/src/SSVOpenHexagon/Core/MenuGame.cpp b/src/SSVOpenHexagon/Core/MenuGame.cpp index ecf39222..7ab255a0 100644 --- a/src/SSVOpenHexagon/Core/MenuGame.cpp +++ b/src/SSVOpenHexagon/Core/MenuGame.cpp @@ -1394,7 +1394,24 @@ void MenuGame::initMenus() visfx.create<i::Toggle>("flash", &Config::getFlash, &Config::setFlash); visfx.create<i::Slider>("shake mult.", &Config::getCameraShakeMultiplier, &Config::setCameraShakeMultiplier, 0.f, 5.f, 0.1f); - visfx.create<i::GoBack>("back"); + + auto& playervisfx(optionsMenu.createCategory("player visual fxs")); + gfx.create<i::Goto>("player visual fxs", playervisfx); + playervisfx.create<i::Slider>("angle tilt mult.", + &Config::getAngleTiltIntensity, &Config::setAngleTiltIntensity, 0.f, + 5.f, 0.1f); + playervisfx.create<i::Toggle>( + "show trail", &Config::getShowPlayerTrail, &Config::setShowPlayerTrail); + playervisfx.create<i::Slider>("trail alpha", &Config::getPlayerTrailAlpha, + &Config::setPlayerTrailAlpha, 0, 255, 5); + playervisfx.create<i::Slider>("trail scale", &Config::getPlayerTrailScale, + &Config::setPlayerTrailScale, 0.05f, 1.f, 0.05f); + playervisfx.create<i::Slider>("trail decay", &Config::getPlayerTrailDecay, + &Config::setPlayerTrailDecay, 0.5f, 50.f, 2.5f); + playervisfx.create<i::Toggle>("trail has swap color", + &Config::getPlayerTrailHasSwapColor, + &Config::setPlayerTrailHasSwapColor); + playervisfx.create<i::GoBack>("back"); auto& fps(optionsMenu.createCategory("fps settings")); gfx.create<i::Goto>("fps settings", fps); @@ -2650,8 +2667,13 @@ void MenuGame::update(ssvu::FT mFT) auto& items = getCurrentMenu()->getItems(); if(static_cast<int>(items.size()) > *mustUseMenuItem) { - playSoundOverride("beep.ogg"); - items.at(*mustUseMenuItem)->exec(); + ssvms::ItemBase& item = *items.at(*mustUseMenuItem); + + if(item.isEnabled()) + { + playSoundOverride("beep.ogg"); + item.exec(); + } } } @@ -3711,7 +3733,7 @@ void MenuGame::drawMainMenu( // TODO (P2): cleanup mouse control if(mouseOverlap && !mustUseMenuItem.has_value() && - mouseLeftRisingEdge()) + mouseLeftRisingEdge() && items[i]->isEnabled()) { mustUseMenuItem = i; } diff --git a/src/SSVOpenHexagon/Global/Config.cpp b/src/SSVOpenHexagon/Global/Config.cpp index 84a15c67..11ee4577 100644 --- a/src/SSVOpenHexagon/Global/Config.cpp +++ b/src/SSVOpenHexagon/Global/Config.cpp @@ -215,6 +215,12 @@ using cil = std::initializer_list<cmb>; X(lastLoginUsername, std::string, "last_login_username", "") \ X(showLoginAtStartup, bool, "show_login_at_startup", false) \ X(cameraShakeMultiplier, float, "camera_shake_multiplier", 1.f) \ + X(angleTiltIntensity, float, "angle_tilt_intensity", 1.f) \ + X(showPlayerTrail, bool, "show_player_trail", true) \ + X(playerTrailAlpha, uint, "player_trail_alpha", 35) \ + X(playerTrailScale, float, "player_trail_scale", 0.9f) \ + X(playerTrailDecay, float, "player_trail_decay", 3.0f) \ + X(playerTrailHasSwapColor, bool, "player_trail_has_swap_color", true) \ X_LINKEDVALUES_BINDS namespace hg::Config { @@ -736,6 +742,36 @@ void setCameraShakeMultiplier(float x) cameraShakeMultiplier() = x; } +void setAngleTiltIntensity(float x) +{ + angleTiltIntensity() = x; +} + +void setShowPlayerTrail(bool x) +{ + showPlayerTrail() = x; +} + +void setPlayerTrailAlpha(unsigned int x) +{ + playerTrailAlpha() = x; +} + +void setPlayerTrailScale(float x) +{ + playerTrailScale() = x; +} + +void setPlayerTrailDecay(float x) +{ + playerTrailDecay() = x; +} + +void setPlayerTrailHasSwapColor(bool x) +{ + playerTrailHasSwapColor() = x; +} + [[nodiscard]] bool getOfficial() { return official(); @@ -1066,6 +1102,36 @@ void setCameraShakeMultiplier(float x) return cameraShakeMultiplier(); } +[[nodiscard]] float getAngleTiltIntensity() +{ + return angleTiltIntensity(); +} + +[[nodiscard]] bool getShowPlayerTrail() +{ + return showPlayerTrail(); +} + +[[nodiscard]] unsigned int getPlayerTrailAlpha() +{ + return playerTrailAlpha(); +} + +[[nodiscard]] float getPlayerTrailScale() +{ + return playerTrailScale(); +} + +[[nodiscard]] float getPlayerTrailDecay() +{ + return playerTrailDecay(); +} + +[[nodiscard]] bool getPlayerTrailHasSwapColor() +{ + return playerTrailHasSwapColor(); +} + //*********************************************************** // // KEYBOARD/MOUSE BINDS diff --git a/webpage/index.html b/webpage/index.html index 87ab1613..a24acb82 100644 --- a/webpage/index.html +++ b/webpage/index.html @@ -11237,27 +11237,33 @@ style="color: black; background-color: white; padding-top:0rem!important;padding-bottom:3rem!important;"> <div class="container"> <div class="row"> - <div class="col-lg-4 col-md-6 text-center"> + <div class="col-lg-3 col-md-6 text-center"> <div class="mt-5"> - <h3 class="h4 mb-2" style="font-weight: 900;">Only Three Buttons</h3> - <p class="mb-0" style="font-weight: 500">Open Hexagon is easy to learn but extremely hard to - master. There are - three main actions you can perform: <i>rotate clockwise</i>, <i>rotate - counter-clockwise</i>, and <i>swap by 180° degrees</i>. + <h3 class="h4 mb-2" style="font-weight: 900;">Only Four Buttons</h3> + <p class="mb-0" style="font-weight: 500">Open Hexagon is easy to learn but hard to + master. There are four main actions you can perform: <i>rotate clockwise</i>, <i>rotate + counter-clockwise</i>, <i>swap by 180° degrees</i>, and <i>focus</i>. </p> </div> </div> - <div class="col-lg-4 col-md-6 text-center"> + <div class="col-lg-3 col-md-6 text-center"> <div class="mt-5"> <h3 class="h4 mb-2" style="font-weight: 900;">Only One Goal: Survive</h3> <p class="mb-0" style="font-weight: 500">Simplicity and minimalism are key features of Open Hexagon. While - certain levels might have special quirks and mechanics, the goal is always the same: + some levels might have special quirks and mechanics, the goal is always the same: <i>survive</i> as long as possible.</p> </div> </div> - <div class="col-lg-4 col-md-6 text-center"> + <div class="col-lg-3 col-md-6 text-center"> + <div class="mt-5"> + <h3 class="h4 mb-2" style="font-weight: 900;">Innovative Mechanics</h3> + <p class="mb-0" style="font-weight: 500" ;>The game's main concept comes from <i>Super Hexagon</i> by <i>Terry Cavanagh</i>. Open Hexagon expands on the formula by introducing a <i>180° swap</i>, <i>curving/accelerating walls</i>, <i>scripting</i>, and much more. + </p> + </div> + </div> + <div class="col-lg-3 col-md-6 text-center"> <div class="mt-5"> <h3 class="h4 mb-2" style="font-weight: 900;">Designed For Modding</h3> <p class="mb-0" style="font-weight: 500" ;>Open Hexagon was developed with the goal of being @@ -11279,11 +11285,11 @@ <div class="container"> <div class="row justify-content-center"> - <iframe width="560" height="315" src="https://www.youtube.com/embed/TlSJZlQrVJ4" + <iframe width="560" height="315" src="https://www.youtube.com/embed/06y7mEsAMHM" style="margin-bottom: 4rem; margin-right: 1rem" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> - <iframe width="560" height="315" src="https://www.youtube.com/embed/gSRoCNDOvkA" + <iframe width="560" height="315" src="https://www.youtube.com/embed/h4Jfj3lzWD4" style="margin-bottom: 4rem" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> @@ -11447,7 +11453,7 @@ <!-- Footer--> <footer class="bg-primary py-5"> <div class="container"> - <div class="text-center text-white">Copyright © 2020 - Vittorio Romeo</div> + <div class="text-center text-white">Copyright © 2020-2021 - Vittorio Romeo</div> </div> </footer> <!-- Bootstrap core JS--> |