Added to_sql

This commit is contained in:
Dr. Patrick Urbanke
2025-05-09 18:17:51 +02:00
parent 650dd8a026
commit b2f4192a4f
18 changed files with 214 additions and 40 deletions
+2
View File
@@ -2,7 +2,9 @@
#define SQLGEN_HPP_
#include "sqlgen/Connection.hpp"
#include "sqlgen/CreateTable.hpp"
#include "sqlgen/Flatten.hpp"
#include "sqlgen/Insert.hpp"
#include "sqlgen/Iterator.hpp"
#include "sqlgen/IteratorBase.hpp"
#include "sqlgen/Literal.hpp"
+15
View File
@@ -0,0 +1,15 @@
#ifndef SQLGEN_CREATETABLE_HPP_
#define SQLGEN_CREATETABLE_HPP_
#include <rfl.hpp>
namespace sqlgen {
/// Helper class for to_sql.
template <class T>
struct CreateTable {};
}; // namespace sqlgen
#endif
+15
View File
@@ -0,0 +1,15 @@
#ifndef SQLGEN_INSERT_HPP_
#define SQLGEN_INSERT_HPP_
#include <rfl.hpp>
namespace sqlgen {
/// Helper class for to_sql.
template <class T>
struct Insert {};
}; // namespace sqlgen
#endif
+1
View File
@@ -4,5 +4,6 @@
#include "../sqlgen.hpp"
#include "postgres/Credentials.hpp"
#include "postgres/connect.hpp"
#include "postgres/to_sql.hpp"
#endif
+2 -2
View File
@@ -41,11 +41,11 @@ class Connection : public sqlgen::Connection {
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query) final;
std::string to_sql(const dynamic::Statement& _stmt) noexcept final {
return postgres::to_sql(_stmt);
return postgres::to_sql_impl(_stmt);
}
Result<Nothing> start_write(const dynamic::Insert& _stmt) final {
return execute(to_sql(_stmt));
return execute(postgres::to_sql_impl(_stmt));
}
Result<Nothing> end_write() final;
+13 -1
View File
@@ -2,13 +2,25 @@
#define SQLGEN_POSTGRES_TO_SQL_HPP_
#include <string>
#include <type_traits>
#include "../dynamic/Statement.hpp"
#include "../transpilation/to_sql.hpp"
namespace sqlgen::postgres {
/// Transpiles a dynamic general SQL statement to the postgres dialect.
std::string to_sql(const dynamic::Statement& _stmt) noexcept;
std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept;
/// Transpiles any SQL statement to the postgres dialect.
template <class T>
std::string to_sql(const T& _t) noexcept {
if constexpr (std::is_same_v<std::remove_cvref_t<T>, dynamic::Statement>) {
return to_sql_impl(_t);
} else {
return to_sql_impl(transpilation::to_sql(_t));
}
}
} // namespace sqlgen::postgres
+1 -1
View File
@@ -37,7 +37,7 @@ class Connection : public sqlgen::Connection {
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query) final;
std::string to_sql(const dynamic::Statement& _stmt) noexcept final {
return sqlite::to_sql(_stmt);
return sqlite::to_sql_impl(_stmt);
}
Result<Nothing> start_write(const dynamic::Insert& _stmt) final;
+12 -1
View File
@@ -4,11 +4,22 @@
#include <string>
#include "../dynamic/Statement.hpp"
#include "../transpilation/to_sql.hpp"
namespace sqlgen::sqlite {
/// Transpiles a dynamic general SQL statement to the sqlite dialect.
std::string to_sql(const dynamic::Statement& _stmt) noexcept;
std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept;
/// Transpiles any SQL statement to the sqlite dialect.
template <class T>
std::string to_sql(const T& _t) noexcept {
if constexpr (std::is_same_v<std::remove_cvref_t<T>, dynamic::Statement>) {
return to_sql_impl(_t);
} else {
return to_sql_impl(transpilation::to_sql(_t));
}
}
} // namespace sqlgen::sqlite
+48
View File
@@ -0,0 +1,48 @@
#ifndef SQLGEN_TRANSPILATION_TO_SQL_HPP_
#define SQLGEN_TRANSPILATION_TO_SQL_HPP_
#include <vector>
#include "../CreateTable.hpp"
#include "../Insert.hpp"
#include "../dynamic/Statement.hpp"
#include "../read.hpp"
#include "to_create_table.hpp"
#include "to_insert.hpp"
#include "to_select_from.hpp"
#include "value_t.hpp"
namespace sqlgen::transpilation {
template <class T>
struct ToSQL;
template <class T>
struct ToSQL<CreateTable<T>> {
dynamic::Statement operator()(const auto&) const {
return to_create_table<T>();
}
};
template <class T>
struct ToSQL<Insert<T>> {
dynamic::Statement operator()(const auto&) const { return to_insert<T>(); }
};
template <class ContainerType, class WhereType, class OrderByType,
class LimitType>
struct ToSQL<Read<ContainerType, WhereType, OrderByType, LimitType>> {
dynamic::Statement operator()(const auto& _read) const {
return to_select_from<value_t<ContainerType>, WhereType, OrderByType,
LimitType>(_read.where_, _read.limit_);
}
};
template <class T>
dynamic::Statement to_sql(const T& _t) {
return ToSQL<std::remove_cvref_t<T>>{}(_t);
}
} // namespace sqlgen::transpilation
#endif
+1 -1
View File
@@ -46,7 +46,7 @@ typename Connection::ConnPtr Connection::make_conn(
}
Result<Ref<IteratorBase>> Connection::read(const dynamic::SelectFrom& _query) {
const auto sql = postgres::to_sql(_query);
const auto sql = postgres::to_sql_impl(_query);
try {
return Ref<IteratorBase>(Ref<Iterator>::make(sql, conn_));
} catch (std::exception& e) {
+1 -1
View File
@@ -223,7 +223,7 @@ std::string select_from_to_sql(const dynamic::SelectFrom& _stmt) noexcept {
return stream.str();
}
std::string to_sql(const dynamic::Statement& _stmt) noexcept {
std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept {
return _stmt.visit([&](const auto& _s) -> std::string {
using S = std::remove_cvref_t<decltype(_s)>;
if constexpr (std::is_same_v<S, dynamic::CreateTable>) {
+2 -2
View File
@@ -42,7 +42,7 @@ typename Connection::ConnPtr Connection::make_conn(const std::string& _fname) {
}
Result<Ref<IteratorBase>> Connection::read(const dynamic::SelectFrom& _query) {
const auto sql = to_sql(_query);
const auto sql = to_sql_impl(_query);
sqlite3_stmt* p_stmt = nullptr;
@@ -70,7 +70,7 @@ Result<Nothing> Connection::start_write(const dynamic::Insert& _stmt) {
".end_write() before you can start another.");
}
const auto sql = to_sql(_stmt);
const auto sql = to_sql_impl(_stmt);
sqlite3_stmt* p_stmt = nullptr;
+1 -1
View File
@@ -210,7 +210,7 @@ std::string select_from_to_sql(const dynamic::SelectFrom& _stmt) noexcept {
return stream.str();
}
std::string to_sql(const dynamic::Statement& _stmt) noexcept {
std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept {
return _stmt.visit([&](const auto& _s) -> std::string {
using S = std::remove_cvref_t<decltype(_s)>;
if constexpr (std::is_same_v<S, dynamic::CreateTable>) {
+23
View File
@@ -0,0 +1,23 @@
#include <gtest/gtest.h>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
namespace test_create_table_dry {
struct TestTable {
std::string field1;
int32_t field2;
sqlgen::PrimaryKey<uint32_t> id;
std::optional<std::string> nullable;
};
TEST(postgres, test_create_table_dry) {
const auto query = sqlgen::CreateTable<TestTable>{};
const auto expected =
R"(CREATE TABLE IF NOT EXISTS "TestTable" ("field1" TEXT NOT NULL, "field2" INTEGER NOT NULL, "id" INTEGER NOT NULL, "nullable" TEXT, PRIMARY KEY ("id"));)";
EXPECT_EQ(sqlgen::postgres::to_sql(query), expected);
}
} // namespace test_create_table_dry
+24
View File
@@ -0,0 +1,24 @@
#include <gtest/gtest.h>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
namespace test_insert_dry {
struct TestTable {
std::string field1;
int32_t field2;
sqlgen::PrimaryKey<uint32_t> id;
std::optional<std::string> nullable;
};
TEST(postgres, test_insert_dry) {
const auto query = sqlgen::Insert<TestTable>{};
const auto expected =
"COPY \"public\".\"TestTable\"(\"field1\", \"field2\", \"id\", "
"\"nullable\") FROM STDIN WITH DELIMITER '\t' NULL '\e' QUOTE '\a';";
EXPECT_EQ(sqlgen::postgres::to_sql(query), expected);
}
} // namespace test_insert_dry
-30
View File
@@ -1,30 +0,0 @@
#include <gtest/gtest.h>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
#include <sqlgen/transpilation/to_select_from.hpp>
namespace test_to_select_from {
struct TestTable {
std::string field1;
int32_t field2;
sqlgen::PrimaryKey<uint32_t> id;
std::optional<std::string> nullable;
};
TEST(postgres, test_to_select_from) {
const auto select_from_stmt =
sqlgen::transpilation::to_select_from<TestTable>();
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
.password = "postgres",
.host = "localhost",
.dbname = "postgres",
.port = 5432};
const auto conn = sqlgen::postgres::connect(credentials).value();
const auto expected =
R"(SELECT "field1", "field2", "id", "nullable" FROM "TestTable";)";
EXPECT_EQ(conn->to_sql(select_from_stmt), expected);
}
} // namespace test_to_select_from
@@ -0,0 +1,24 @@
#include <gtest/gtest.h>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
#include <sqlgen/transpilation/to_select_from.hpp>
namespace test_to_select_from_dry {
struct TestTable {
std::string field1;
int32_t field2;
sqlgen::PrimaryKey<uint32_t> id;
std::optional<std::string> nullable;
};
TEST(postgres, test_to_select_from_dry) {
const auto query = sqlgen::read<std::vector<TestTable>>;
const auto expected =
R"(SELECT "field1", "field2", "id", "nullable" FROM "TestTable";)";
EXPECT_EQ(sqlgen::postgres::to_sql(query), expected);
}
} // namespace test_to_select_from_dry
+29
View File
@@ -0,0 +1,29 @@
#include <gtest/gtest.h>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
#include <vector>
namespace test_where_dry {
struct Person {
sqlgen::PrimaryKey<uint32_t> id;
std::string first_name;
std::string last_name;
int age;
};
TEST(postgres, test_where_dry) {
using namespace sqlgen;
const auto query = sqlgen::read<std::vector<Person>> |
where("age"_c < 18 and "first_name"_c != "Hugo") |
order_by("age"_c);
const auto expected =
R"(SELECT "id", "first_name", "last_name", "age" FROM "Person" WHERE ("age" < 18) AND ("first_name" != 'Hugo') ORDER BY "age";)";
EXPECT_EQ(sqlgen::postgres::to_sql(query), expected);
}
} // namespace test_where_dry