Added drop

This commit is contained in:
Dr. Patrick Urbanke
2025-05-10 16:32:34 +02:00
parent be53c24465
commit 863b861090
13 changed files with 256 additions and 5 deletions

View File

@@ -12,7 +12,7 @@ sqlgen is closely integrated with our sister project [reflect-cpp](https://githu
## Documentation
Click [here](docs/README.md).
Click [here](docs).
## Inserting data
@@ -141,6 +141,16 @@ Without `AlphaNumeric` validation, this code would be vulnerable to SQL injectio
get_people(conn, "Homer' OR '1'='1"); // Attempt to bypass filtering
```
## Dropping a table
```cpp
using namespace sqlgen;
const auto query = drop<People> | if_exists;
query(conn).value();
```
## Deleting data
```cpp

View File

@@ -74,7 +74,11 @@ using namespace sqlgen;
const auto query = delete_from<Person> |
where("age"_c >= 18 and "last_name"_c == "Simpson");
const auto result = query(conn).value();
const auto result = query(conn);
if (!result) {
// Error handling
}
```
This generates the following SQL:
@@ -91,12 +95,12 @@ if you do not want to do that, you can rewrite the example above as follows:
const auto query = sqlgen::delete_from<Person> |
sqlgen::where(sqlgen::col<"age"> >= 18 and sqlgen::col<"last_name"> == "Simpson");
const auto result = query(conn).value();
const auto result = query(conn);
```
## Notes
- The `where` clause is optional - if omitted, all records will be deleted
- The `Result<Nothing>` type provides error handling; use `.value()` to extract the result (will throw an exception if there's an error) or refer to the documentation on results for other forms of error handling.
- The `Result<Nothing>` type provides error handling; use `.value()` to extract the result (will throw an exception if there's an error) or handle errors as needed or refer to the documentation on `sqlgen::Result<...>` for other forms of error handling.
- `"..."_c` refers to the name of the column

View File

@@ -16,6 +16,8 @@
#include "sqlgen/Varchar.hpp"
#include "sqlgen/col.hpp"
#include "sqlgen/delete_from.hpp"
#include "sqlgen/drop.hpp"
#include "sqlgen/if_exists.hpp"
#include "sqlgen/limit.hpp"
#include "sqlgen/order_by.hpp"
#include "sqlgen/patterns.hpp"

45
include/sqlgen/drop.hpp Normal file
View File

@@ -0,0 +1,45 @@
#ifndef SQLGEN_DROP_HPP_
#define SQLGEN_DROP_HPP_
#include <type_traits>
#include "Connection.hpp"
#include "Ref.hpp"
#include "Result.hpp"
#include "transpilation/to_drop.hpp"
namespace sqlgen {
template <class ValueType>
Result<Nothing> drop_impl(const Ref<Connection>& _conn, const bool _if_exists) {
const auto query = transpilation::to_drop<ValueType>(_if_exists);
return _conn->execute(_conn->to_sql(query));
}
template <class ValueType>
Result<Nothing> drop_impl(const Result<Ref<Connection>>& _res,
const bool _if_exists) {
return _res.and_then([&](const auto& _conn) {
return drop_impl<ValueType>(_conn, _if_exists);
});
}
template <class ValueType>
struct Drop {
Result<Nothing> operator()(const auto& _conn) const noexcept {
try {
return drop_impl<ValueType>(_conn, if_exists_);
} catch (std::exception& e) {
return error(e.what());
}
}
bool if_exists_ = false;
};
template <class ContainerType>
const auto drop = Drop<ContainerType>{};
} // namespace sqlgen
#endif

View File

@@ -0,0 +1,17 @@
#ifndef SQLGEN_DYNAMIC_DROP_HPP_
#define SQLGEN_DYNAMIC_DROP_HPP_
#include <optional>
#include "Table.hpp"
namespace sqlgen::dynamic {
struct Drop {
bool if_exists = true;
Table table;
};
} // namespace sqlgen::dynamic
#endif

View File

@@ -5,13 +5,14 @@
#include "CreateTable.hpp"
#include "DeleteFrom.hpp"
#include "Drop.hpp"
#include "Insert.hpp"
#include "SelectFrom.hpp"
namespace sqlgen::dynamic {
using Statement =
rfl::TaggedUnion<"stmt", CreateTable, DeleteFrom, Insert, SelectFrom>;
rfl::TaggedUnion<"stmt", CreateTable, DeleteFrom, Drop, Insert, SelectFrom>;
} // namespace sqlgen::dynamic

View File

@@ -0,0 +1,21 @@
#ifndef SQLGEN_IF_EXISTS_HPP_
#define SQLGEN_IF_EXISTS_HPP_
#include "drop.hpp"
namespace sqlgen {
struct IfExists {};
template <class OtherType>
auto operator|(const OtherType& _o, const IfExists&) {
auto o = _o;
o.if_exists_ = true;
return o;
}
inline const auto if_exists = IfExists{};
} // namespace sqlgen
#endif

View File

@@ -0,0 +1,29 @@
#ifndef SQLGEN_TRANSPILATION_TO_DROP_HPP_
#define SQLGEN_TRANSPILATION_TO_DROP_HPP_
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "../Result.hpp"
#include "../dynamic/Drop.hpp"
#include "../dynamic/Table.hpp"
#include "get_schema.hpp"
#include "get_tablename.hpp"
#include "to_condition.hpp"
namespace sqlgen::transpilation {
template <class T>
requires std::is_class_v<std::remove_cvref_t<T>> &&
std::is_aggregate_v<std::remove_cvref_t<T>>
dynamic::Drop to_drop(const bool _if_exists) {
return dynamic::Drop{.if_exists = _if_exists,
.table = dynamic::Table{.name = get_tablename<T>(),
.schema = get_schema<T>()}};
}
} // namespace sqlgen::transpilation
#endif

View File

@@ -6,10 +6,12 @@
#include "../CreateTable.hpp"
#include "../Insert.hpp"
#include "../delete_from.hpp"
#include "../drop.hpp"
#include "../dynamic/Statement.hpp"
#include "../read.hpp"
#include "to_create_table.hpp"
#include "to_delete_from.hpp"
#include "to_drop.hpp"
#include "to_insert.hpp"
#include "to_select_from.hpp"
#include "value_t.hpp"
@@ -33,6 +35,13 @@ struct ToSQL<DeleteFrom<T, WhereType>> {
}
};
template <class T>
struct ToSQL<Drop<T>> {
dynamic::Statement operator()(const auto& _drop) const {
return to_drop<T>(_drop.if_exists_);
}
};
template <class T>
struct ToSQL<Insert<T>> {
dynamic::Statement operator()(const auto&) const { return to_insert<T>(); }

View File

@@ -26,6 +26,8 @@ std::string create_table_to_sql(const dynamic::CreateTable& _stmt) noexcept;
std::string delete_from_to_sql(const dynamic::DeleteFrom& _stmt) noexcept;
std::string drop_to_sql(const dynamic::Drop& _stmt) noexcept;
std::vector<std::string> get_primary_keys(
const dynamic::CreateTable& _stmt) noexcept;
@@ -180,6 +182,25 @@ std::string delete_from_to_sql(const dynamic::DeleteFrom& _stmt) noexcept {
return stream.str();
}
std::string drop_to_sql(const dynamic::Drop& _stmt) noexcept {
std::stringstream stream;
stream << "DROP TABLE ";
if (_stmt.if_exists) {
stream << "IF EXISTS ";
}
if (_stmt.table.schema) {
stream << wrap_in_quotes(*_stmt.table.schema) << ".";
}
stream << wrap_in_quotes(_stmt.table.name);
stream << ";";
return stream.str();
}
std::vector<std::string> get_primary_keys(
const dynamic::CreateTable& _stmt) noexcept {
using namespace std::ranges::views;
@@ -253,6 +274,9 @@ std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept {
} else if constexpr (std::is_same_v<S, dynamic::DeleteFrom>) {
return delete_from_to_sql(_s);
} else if constexpr (std::is_same_v<S, dynamic::Drop>) {
return drop_to_sql(_s);
} else if constexpr (std::is_same_v<S, dynamic::Insert>) {
return insert_to_sql(_s);

View File

@@ -22,6 +22,8 @@ std::string create_table_to_sql(const dynamic::CreateTable& _stmt) noexcept;
std::string delete_from_to_sql(const dynamic::DeleteFrom& _stmt) noexcept;
std::string drop_to_sql(const dynamic::Drop& _stmt) noexcept;
std::string insert_to_sql(const dynamic::Insert& _stmt) noexcept;
std::string properties_to_sql(const dynamic::types::Properties& _p) noexcept;
@@ -152,6 +154,25 @@ std::string delete_from_to_sql(const dynamic::DeleteFrom& _stmt) noexcept {
return stream.str();
}
std::string drop_to_sql(const dynamic::Drop& _stmt) noexcept {
std::stringstream stream;
stream << "DROP TABLE ";
if (_stmt.if_exists) {
stream << "IF EXISTS ";
}
if (_stmt.table.schema) {
stream << "\"" << *_stmt.table.schema << "\".";
}
stream << "\"" << _stmt.table.name << "\"";
stream << ";";
return stream.str();
}
std::string insert_to_sql(const dynamic::Insert& _stmt) noexcept {
using namespace std::ranges::views;
@@ -240,6 +261,9 @@ std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept {
} else if constexpr (std::is_same_v<S, dynamic::DeleteFrom>) {
return delete_from_to_sql(_s);
} else if constexpr (std::is_same_v<S, dynamic::Drop>) {
return drop_to_sql(_s);
} else if constexpr (std::is_same_v<S, dynamic::Insert>) {
return insert_to_sql(_s);

View File

@@ -0,0 +1,25 @@
#include <gtest/gtest.h>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
#include <sqlgen/transpilation/to_select_from.hpp>
namespace test_drop_dry {
struct TestTable {
std::string field1;
int32_t field2;
sqlgen::PrimaryKey<uint32_t> id;
std::optional<std::string> nullable;
};
TEST(postgres, test_drop_dry) {
using namespace sqlgen;
const auto query = drop<TestTable> | if_exists;
const auto expected = R"(DROP TABLE IF EXISTS "TestTable";)";
EXPECT_EQ(sqlgen::postgres::to_sql(query), expected);
}
} // namespace test_drop_dry

View File

@@ -0,0 +1,40 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/sqlite.hpp>
#include <vector>
namespace test_drop {
struct Person {
sqlgen::PrimaryKey<uint32_t> id;
std::string first_name;
std::string last_name;
int age;
};
TEST(sqlite, test_drop) {
const auto people1 = std::vector<Person>(
{Person{
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
Person{
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0},
Person{
.id = 4, .first_name = "Hugo", .last_name = "Simpson", .age = 10}});
const auto conn = sqlgen::sqlite::connect();
sqlgen::write(conn, people1);
using namespace sqlgen;
const auto query = drop<Person> | if_exists;
query(conn).value();
}
} // namespace test_drop