From b7598c55a640b046a0a872cf698db1bca25318de Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 18 May 2025 14:47:30 +0200 Subject: [PATCH] Added more formal support for create table --- CMakeLists.txt | 4 +- docs/README.md | 3 +- docs/to_sql.md | 2 +- include/sqlgen.hpp | 3 +- include/sqlgen/CreateTable.hpp | 15 ------ include/sqlgen/create_table.hpp | 46 +++++++++++++++++++ include/sqlgen/if_not_exists.hpp | 19 ++++++++ .../sqlgen/transpilation/to_create_table.hpp | 4 +- include/sqlgen/transpilation/to_sql.hpp | 2 +- sqlgen-config.cmake.in | 7 ++- tests/sqlite/test_create_table.cpp | 30 ++++++++++++ 11 files changed, 111 insertions(+), 24 deletions(-) delete mode 100644 include/sqlgen/CreateTable.hpp create mode 100644 include/sqlgen/create_table.hpp create mode 100644 include/sqlgen/if_not_exists.hpp create mode 100644 tests/sqlite/test_create_table.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ab3cb4f..85237ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,14 +103,14 @@ if (PROJECT_IS_TOP_LEVEL) DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/sqlgen" ) - file(GLOB_RECURSE RFL_HEADERS RELATIVE ${CMAKE_CURRENT_LIST_DIR} "${CMAKE_CURRENT_LIST_DIR}/include/*" ) + file(GLOB_RECURSE SQLGEN_HEADERS RELATIVE ${CMAKE_CURRENT_LIST_DIR} "${CMAKE_CURRENT_LIST_DIR}/include/*" ) target_sources(sqlgen PUBLIC FILE_SET sqlgen_headers TYPE HEADERS BASE_DIRS $ $ - FILES ${RFL_HEADERS}) + FILES ${SQLGEN_HEADERS}) install( TARGETS sqlgen diff --git a/docs/README.md b/docs/README.md index 3914024..1cb25e6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,6 +14,7 @@ Welcome to the sqlgen documentation. This guide provides detailed information ab - [sqlgen::read](reading.md) - How to read data from a database - [sqlgen::write](writing.md) - How to write data to a database +- [sqlgen::create_table](create_table.md) - How to create a new table - [sqlgen::update](update.md) - How to update data in a table - [sqlgen::delete_from](delete_from.md) - How to delete data from a table - [sqlgen::drop](drop.md) - How to drop a table @@ -30,4 +31,4 @@ Welcome to the sqlgen documentation. This guide provides detailed information ab - [PostgreSQL](postgres.md) - How to interact with PostgreSQL and compatible databases (Redshift, Aurora, Greenplum) - [SQLite](sqlite.md) - How to interact with SQLite3 -For installation instructions, quick start guide, and usage examples, please refer to the [main README](../README.md). \ No newline at end of file +For installation instructions, quick start guide, and usage examples, please refer to the [main README](../README.md). diff --git a/docs/to_sql.md b/docs/to_sql.md index e47efe6..e4175fa 100644 --- a/docs/to_sql.md +++ b/docs/to_sql.md @@ -91,7 +91,7 @@ struct TestTable { }; // Create table query -const auto create_query = sqlgen::CreateTable{}; +const auto create_query = create_table | if_not_exists; const auto sql = postgres::to_sql(create_query); ``` diff --git a/include/sqlgen.hpp b/include/sqlgen.hpp index 0d852b3..437f747 100644 --- a/include/sqlgen.hpp +++ b/include/sqlgen.hpp @@ -2,7 +2,6 @@ #define SQLGEN_HPP_ #include "sqlgen/Connection.hpp" -#include "sqlgen/CreateTable.hpp" #include "sqlgen/Flatten.hpp" #include "sqlgen/Insert.hpp" #include "sqlgen/Iterator.hpp" @@ -18,9 +17,11 @@ #include "sqlgen/begin_transaction.hpp" #include "sqlgen/col.hpp" #include "sqlgen/commit.hpp" +#include "sqlgen/create_table.hpp" #include "sqlgen/delete_from.hpp" #include "sqlgen/drop.hpp" #include "sqlgen/if_exists.hpp" +#include "sqlgen/if_not_exists.hpp" #include "sqlgen/limit.hpp" #include "sqlgen/order_by.hpp" #include "sqlgen/patterns.hpp" diff --git a/include/sqlgen/CreateTable.hpp b/include/sqlgen/CreateTable.hpp deleted file mode 100644 index 1f59291..0000000 --- a/include/sqlgen/CreateTable.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef SQLGEN_CREATETABLE_HPP_ -#define SQLGEN_CREATETABLE_HPP_ - -#include - -namespace sqlgen { - -/// Helper class for to_sql. -template -struct CreateTable {}; - -}; // namespace sqlgen - -#endif - diff --git a/include/sqlgen/create_table.hpp b/include/sqlgen/create_table.hpp new file mode 100644 index 0000000..223e8f0 --- /dev/null +++ b/include/sqlgen/create_table.hpp @@ -0,0 +1,46 @@ +#ifndef SQLGEN_CREATETABLE_HPP_ +#define SQLGEN_CREATETABLE_HPP_ + +#include + +#include "transpilation/to_create_table.hpp" + +namespace sqlgen { + +template +Result> create_table_impl(const Ref& _conn, + const bool _if_not_exists) { + const auto query = transpilation::to_create_table(_if_not_exists); + return _conn->execute(_conn->to_sql(query)).transform([&](const auto&) { + return _conn; + }); +} + +template +Result> create_table_impl(const Result>& _res, + const bool _if_not_exists) { + return _res.and_then([&](const auto& _conn) { + return create_table_impl(_conn, _if_not_exists); + }); +} + +template +struct CreateTable { + Result> operator()(const auto& _conn) const noexcept { + try { + return create_table_impl(_conn, if_not_exists_); + } catch (std::exception& e) { + return error(e.what()); + } + } + + bool if_not_exists_; +}; + +template +const auto create_table = CreateTable{}; + +}; // namespace sqlgen + +#endif + diff --git a/include/sqlgen/if_not_exists.hpp b/include/sqlgen/if_not_exists.hpp new file mode 100644 index 0000000..301a067 --- /dev/null +++ b/include/sqlgen/if_not_exists.hpp @@ -0,0 +1,19 @@ +#ifndef SQLGEN_IF_NOT_EXISTS_HPP_ +#define SQLGEN_IF_NOT_EXISTS_HPP_ + +namespace sqlgen { + +struct IfNotExists {}; + +template +auto operator|(const OtherType& _o, const IfNotExists&) { + auto o = _o; + o.if_not_exists_ = true; + return o; +} + +inline const auto if_not_exists = IfNotExists{}; + +} // namespace sqlgen + +#endif diff --git a/include/sqlgen/transpilation/to_create_table.hpp b/include/sqlgen/transpilation/to_create_table.hpp index 04ce8b7..cb2be3d 100644 --- a/include/sqlgen/transpilation/to_create_table.hpp +++ b/include/sqlgen/transpilation/to_create_table.hpp @@ -18,7 +18,7 @@ namespace sqlgen::transpilation { template requires std::is_class_v> && std::is_aggregate_v> -dynamic::CreateTable to_create_table() { +dynamic::CreateTable to_create_table(const bool _if_not_exists = true) { using NamedTupleType = rfl::named_tuple_t>; using Fields = typename NamedTupleType::Fields; return dynamic::CreateTable{ @@ -26,7 +26,7 @@ dynamic::CreateTable to_create_table() { dynamic::Table{.name = get_tablename(), .schema = get_schema()}, .columns = make_columns( std::make_integer_sequence>()), - .if_not_exists = true}; + .if_not_exists = _if_not_exists}; } } // namespace sqlgen::transpilation diff --git a/include/sqlgen/transpilation/to_sql.hpp b/include/sqlgen/transpilation/to_sql.hpp index 80b0c78..f9d7cdd 100644 --- a/include/sqlgen/transpilation/to_sql.hpp +++ b/include/sqlgen/transpilation/to_sql.hpp @@ -3,8 +3,8 @@ #include -#include "../CreateTable.hpp" #include "../Insert.hpp" +#include "../create_table.hpp" #include "../delete_from.hpp" #include "../drop.hpp" #include "../dynamic/Statement.hpp" diff --git a/sqlgen-config.cmake.in b/sqlgen-config.cmake.in index d1cb723..2b35e3d 100644 --- a/sqlgen-config.cmake.in +++ b/sqlgen-config.cmake.in @@ -1,5 +1,6 @@ @PACKAGE_INIT@ +set(SQLGEN_POSTGRES @SQLGEN_POSTGRES@) set(SQLGEN_SQLITE3 @SQLGEN_SQLITE3@) include(CMakeFindDependencyMacro) @@ -8,8 +9,12 @@ include(${CMAKE_CURRENT_LIST_DIR}/sqlgen-exports.cmake) find_dependency(reflectcpp) +if(SQLGEN_POSTGRES) + find_dependency(PostgreSQL) +endif() + if(SQLGEN_SQLITE3) - find_dependency(sqlite3) + find_dependency(unofficial-sqlite3) endif() check_required_components(sqlgen) diff --git a/tests/sqlite/test_create_table.cpp b/tests/sqlite/test_create_table.cpp new file mode 100644 index 0000000..800a520 --- /dev/null +++ b/tests/sqlite/test_create_table.cpp @@ -0,0 +1,30 @@ +#include + +#include +#include +#include +#include +#include + +namespace test_create_table { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(sqlite, test_create_table) { + using namespace sqlgen; + + const auto people = sqlgen::sqlite::connect() + .and_then(create_table | if_not_exists) + .and_then(sqlgen::read>); + + const std::string expected = R"([])"; + + EXPECT_EQ(rfl::json::write(people), expected); +} + +} // namespace test_create_table