mirror of
https://github.com/getml/sqlgen.git
synced 2026-05-08 08:10:28 -05:00
Added create_index
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include "sqlgen/begin_transaction.hpp"
|
||||
#include "sqlgen/col.hpp"
|
||||
#include "sqlgen/commit.hpp"
|
||||
#include "sqlgen/create_index.hpp"
|
||||
#include "sqlgen/create_table.hpp"
|
||||
#include "sqlgen/delete_from.hpp"
|
||||
#include "sqlgen/drop.hpp"
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef SQLGEN_CREATEINDEX_HPP_
|
||||
#define SQLGEN_CREATEINDEX_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "transpilation/columns_t.hpp"
|
||||
#include "transpilation/to_create_index.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class ValueType, class ColumnsType, class WhereType>
|
||||
Result<Ref<Connection>> create_index_impl(const Ref<Connection>& _conn,
|
||||
const std::string& _name,
|
||||
const bool _unique,
|
||||
const bool _if_not_exists,
|
||||
const WhereType& _where) {
|
||||
const auto query =
|
||||
transpilation::to_create_index<ValueType, ColumnsType, WhereType>(
|
||||
_name, _unique, _if_not_exists, _where);
|
||||
return _conn->execute(_conn->to_sql(query)).transform([&](const auto&) {
|
||||
return _conn;
|
||||
});
|
||||
}
|
||||
|
||||
template <class ValueType, class ColumnsType, class WhereType>
|
||||
Result<Ref<Connection>> create_index_impl(const Result<Ref<Connection>>& _res,
|
||||
const std::string& _name,
|
||||
const bool _unique,
|
||||
const bool _if_not_exists,
|
||||
const WhereType& _where) {
|
||||
return _res.and_then([&](const auto& _conn) {
|
||||
return create_index_impl<ValueType, ColumnsType, WhereType>(
|
||||
_conn, _name, _unique, _if_not_exists, _where);
|
||||
});
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name, class ValueType, class WhereType,
|
||||
class... ColTypes>
|
||||
struct CreateIndex {
|
||||
Result<Ref<Connection>> operator()(const auto& _conn) const noexcept {
|
||||
try {
|
||||
return create_index_impl<ValueType,
|
||||
transpilation::columns_t<ValueType, ColTypes...>,
|
||||
WhereType>(_conn, _name.str(), unique_,
|
||||
if_not_exists_, where_);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool unique_ = false;
|
||||
bool if_not_exists_ = false;
|
||||
|
||||
WhereType where_;
|
||||
};
|
||||
|
||||
template <rfl::internal::StringLiteral _name, class ValueType,
|
||||
class... ColTypes>
|
||||
auto create_index(const ColTypes&...) {
|
||||
return CreateIndex<_name, ValueType, Nothing, ColTypes...>{
|
||||
.unique_ = false, .if_not_exists_ = false};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name, class ValueType,
|
||||
class... ColTypes>
|
||||
auto create_unique_index(const ColTypes&...) {
|
||||
return CreateIndex<_name, ValueType, Nothing, ColTypes...>{
|
||||
.unique_ = true, .if_not_exists_ = false};
|
||||
}
|
||||
|
||||
}; // namespace sqlgen
|
||||
|
||||
#endif
|
||||
|
||||
@@ -37,8 +37,8 @@ struct CreateTable {
|
||||
bool if_not_exists_;
|
||||
};
|
||||
|
||||
template <class ContainerType>
|
||||
const auto create_table = CreateTable<ContainerType>{};
|
||||
template <class ValueType>
|
||||
const auto create_table = CreateTable<ValueType>{};
|
||||
|
||||
}; // namespace sqlgen
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef SQLGEN_DYNAMIC_CREATEINDEX_HPP_
|
||||
#define SQLGEN_DYNAMIC_CREATEINDEX_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Column.hpp"
|
||||
#include "Condition.hpp"
|
||||
#include "Table.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
struct CreateIndex {
|
||||
std::string name;
|
||||
|
||||
Table table;
|
||||
std::vector<std::string> columns;
|
||||
|
||||
bool unique = false;
|
||||
bool if_not_exists = true;
|
||||
std::optional<Condition> where = std::nullopt;
|
||||
};
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
#endif
|
||||
@@ -13,7 +13,6 @@ struct CreateTable {
|
||||
Table table;
|
||||
std::vector<Column> columns;
|
||||
bool if_not_exists = true;
|
||||
// TODO: Constraints
|
||||
};
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <rfl.hpp>
|
||||
|
||||
#include "CreateIndex.hpp"
|
||||
#include "CreateTable.hpp"
|
||||
#include "DeleteFrom.hpp"
|
||||
#include "Drop.hpp"
|
||||
@@ -12,8 +13,8 @@
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
using Statement = rfl::TaggedUnion<"stmt", CreateTable, DeleteFrom, Drop,
|
||||
Insert, SelectFrom, Update>;
|
||||
using Statement = rfl::TaggedUnion<"stmt", CreateIndex, CreateTable, DeleteFrom,
|
||||
Drop, Insert, SelectFrom, Update>;
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_COLUMNST_HPP_
|
||||
#define SQLGEN_TRANSPILATION_COLUMNST_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "../col.hpp"
|
||||
#include "Desc.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class... _ColTypes>
|
||||
struct Columns {
|
||||
rfl::Tuple<_ColTypes...> values;
|
||||
|
||||
static std::vector<std::string> to_vec() {
|
||||
return std::vector<std::string>({_ColTypes().name()...});
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class... ColTypes>
|
||||
auto make_columns() {
|
||||
static_assert(all_columns_exist<T, ColTypes...>(), "All columns must exist.");
|
||||
return Columns<ColTypes...>{};
|
||||
}
|
||||
|
||||
template <class T, class... ColTypes>
|
||||
using columns_t = std::invoke_result_t<decltype(make_columns<T, ColTypes...>)>;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_TO_CREATE_INDEX_HPP_
|
||||
#define SQLGEN_TRANSPILATION_TO_CREATE_INDEX_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../dynamic/CreateIndex.hpp"
|
||||
#include "../dynamic/Table.hpp"
|
||||
#include "get_schema.hpp"
|
||||
#include "get_tablename.hpp"
|
||||
#include "to_condition.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T, class ColumnsType, class WhereType>
|
||||
requires std::is_class_v<std::remove_cvref_t<T>> &&
|
||||
std::is_aggregate_v<std::remove_cvref_t<T>>
|
||||
dynamic::CreateIndex to_create_index(const std::string& _name,
|
||||
const bool _unique,
|
||||
const bool _if_not_exists,
|
||||
const WhereType& _where) {
|
||||
return dynamic::CreateIndex{
|
||||
.name = _name,
|
||||
.table =
|
||||
dynamic::Table{.name = get_tablename<T>(), .schema = get_schema<T>()},
|
||||
.columns = ColumnsType::to_vec(),
|
||||
.unique = _unique,
|
||||
.if_not_exists = _if_not_exists,
|
||||
.where = to_condition<std::remove_cvref_t<T>>(_where)};
|
||||
}
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -4,12 +4,15 @@
|
||||
#include <vector>
|
||||
|
||||
#include "../Insert.hpp"
|
||||
#include "../create_index.hpp"
|
||||
#include "../create_table.hpp"
|
||||
#include "../delete_from.hpp"
|
||||
#include "../drop.hpp"
|
||||
#include "../dynamic/Statement.hpp"
|
||||
#include "../read.hpp"
|
||||
#include "../update.hpp"
|
||||
#include "columns_t.hpp"
|
||||
#include "to_create_index.hpp"
|
||||
#include "to_create_table.hpp"
|
||||
#include "to_delete_from.hpp"
|
||||
#include "to_drop.hpp"
|
||||
@@ -23,6 +26,17 @@ namespace sqlgen::transpilation {
|
||||
template <class T>
|
||||
struct ToSQL;
|
||||
|
||||
template <rfl::internal::StringLiteral _name, class ValueType, class WhereType,
|
||||
class... ColTypes>
|
||||
struct ToSQL<CreateIndex<_name, ValueType, WhereType, ColTypes...>> {
|
||||
dynamic::Statement operator()(const auto& _create_index) const {
|
||||
return transpilation::to_create_index<
|
||||
ValueType, columns_t<ValueType, ColTypes...>, WhereType>(
|
||||
_name.str(), _create_index.unique_, _create_index.if_not_exists_,
|
||||
_create_index.where_);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct ToSQL<CreateTable<T>> {
|
||||
dynamic::Statement operator()(const auto&) const {
|
||||
|
||||
@@ -22,6 +22,8 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept;
|
||||
|
||||
std::string column_to_sql_definition(const dynamic::Column& _col) noexcept;
|
||||
|
||||
std::string create_index_to_sql(const dynamic::CreateIndex& _stmt) noexcept;
|
||||
|
||||
std::string create_table_to_sql(const dynamic::CreateTable& _stmt) noexcept;
|
||||
|
||||
std::string delete_from_to_sql(const dynamic::DeleteFrom& _stmt) noexcept;
|
||||
@@ -130,6 +132,45 @@ std::string column_to_sql_definition(const dynamic::Column& _col) noexcept {
|
||||
_col.type.visit([](const auto& _t) { return _t.properties; }));
|
||||
}
|
||||
|
||||
std::string create_index_to_sql(const dynamic::CreateIndex& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
if (_stmt.unique) {
|
||||
stream << "CREATE UNIQUE INDEX ";
|
||||
} else {
|
||||
stream << "CREATE INDEX ";
|
||||
}
|
||||
|
||||
if (_stmt.if_not_exists) {
|
||||
stream << "IF NOT EXISTS ";
|
||||
}
|
||||
|
||||
stream << "\"" << _stmt.name << "\" ";
|
||||
|
||||
stream << "ON ";
|
||||
|
||||
if (_stmt.table.schema) {
|
||||
stream << "\"" << *_stmt.table.schema << "\".";
|
||||
}
|
||||
stream << "\"" << _stmt.table.name << "\"";
|
||||
|
||||
stream << "(";
|
||||
stream << internal::strings::join(
|
||||
", ",
|
||||
internal::collect::vector(_stmt.columns | transform(wrap_in_quotes)));
|
||||
stream << ")";
|
||||
|
||||
if (_stmt.where) {
|
||||
stream << " WHERE " << condition_to_sql(*_stmt.where);
|
||||
}
|
||||
|
||||
stream << ";";
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string create_table_to_sql(const dynamic::CreateTable& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
@@ -271,7 +312,10 @@ 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>) {
|
||||
if constexpr (std::is_same_v<S, dynamic::CreateIndex>) {
|
||||
return create_index_to_sql(_s);
|
||||
|
||||
} else if constexpr (std::is_same_v<S, dynamic::CreateTable>) {
|
||||
return create_table_to_sql(_s);
|
||||
|
||||
} else if constexpr (std::is_same_v<S, dynamic::DeleteFrom>) {
|
||||
|
||||
@@ -18,6 +18,8 @@ std::string condition_to_sql(const dynamic::Condition& _cond) noexcept;
|
||||
template <class ConditionType>
|
||||
std::string condition_to_sql_impl(const ConditionType& _condition) noexcept;
|
||||
|
||||
std::string create_index_to_sql(const dynamic::CreateIndex& _stmt) noexcept;
|
||||
|
||||
std::string create_table_to_sql(const dynamic::CreateTable& _stmt) noexcept;
|
||||
|
||||
std::string delete_from_to_sql(const dynamic::DeleteFrom& _stmt) noexcept;
|
||||
@@ -112,6 +114,46 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept {
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string create_index_to_sql(const dynamic::CreateIndex& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
const auto in_quotes = [](const std::string& _str) -> std::string {
|
||||
return "\"" + _str + "\"";
|
||||
};
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
if (_stmt.unique) {
|
||||
stream << "CREATE UNIQUE INDEX ";
|
||||
} else {
|
||||
stream << "CREATE INDEX ";
|
||||
}
|
||||
|
||||
if (_stmt.if_not_exists) {
|
||||
stream << "IF NOT EXISTS ";
|
||||
}
|
||||
|
||||
if (_stmt.table.schema) {
|
||||
stream << "\"" << *_stmt.table.schema << "\".";
|
||||
}
|
||||
stream << "\"" << _stmt.name << "\" ";
|
||||
|
||||
stream << "ON " << "\"" << _stmt.table.name << "\"";
|
||||
|
||||
stream << "(";
|
||||
stream << internal::strings::join(
|
||||
", ", internal::collect::vector(_stmt.columns | transform(in_quotes)));
|
||||
stream << ")";
|
||||
|
||||
if (_stmt.where) {
|
||||
stream << " WHERE " << condition_to_sql(*_stmt.where);
|
||||
}
|
||||
|
||||
stream << ";";
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string create_table_to_sql(const dynamic::CreateTable& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
@@ -259,7 +301,10 @@ std::string select_from_to_sql(const dynamic::SelectFrom& _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>) {
|
||||
if constexpr (std::is_same_v<S, dynamic::CreateIndex>) {
|
||||
return create_index_to_sql(_s);
|
||||
|
||||
} else if constexpr (std::is_same_v<S, dynamic::CreateTable>) {
|
||||
return create_table_to_sql(_s);
|
||||
|
||||
} else if constexpr (std::is_same_v<S, dynamic::DeleteFrom>) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_create_index {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(postgres, test_create_index) {
|
||||
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
|
||||
.password = "password",
|
||||
.host = "localhost",
|
||||
.dbname = "postgres"};
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto people = sqlgen::postgres::connect(credentials)
|
||||
.and_then(drop<Person> | if_exists)
|
||||
.and_then(create_table<Person> | if_not_exists)
|
||||
.and_then(create_index<"test_table_ix", Person>(
|
||||
"first_name"_c, "last_name"_c) |
|
||||
if_not_exists)
|
||||
.and_then(sqlgen::read<std::vector<Person>>)
|
||||
.value();
|
||||
|
||||
const std::string expected = R"([])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(people), expected);
|
||||
}
|
||||
|
||||
} // namespace test_create_index
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
|
||||
namespace test_create_index_dry {
|
||||
|
||||
struct TestTable {
|
||||
std::string field1;
|
||||
int32_t field2;
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::optional<std::string> nullable;
|
||||
};
|
||||
|
||||
TEST(postgres, test_create_index_dry) {
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto query =
|
||||
create_index<"test_table_ix", TestTable>("field1"_c, "field2"_c) |
|
||||
if_not_exists;
|
||||
|
||||
const auto expected =
|
||||
R"(CREATE INDEX IF NOT EXISTS "test_table_ix" ON "TestTable"("field1", "field2");)";
|
||||
|
||||
EXPECT_EQ(sqlgen::postgres::to_sql(query), expected);
|
||||
}
|
||||
} // namespace test_create_index_dry
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_create_table {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(postgres, test_create_table) {
|
||||
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
|
||||
.password = "password",
|
||||
.host = "localhost",
|
||||
.dbname = "postgres"};
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto people = sqlgen::postgres::connect(credentials)
|
||||
.and_then(drop<Person> | if_exists)
|
||||
.and_then(create_table<Person> | if_not_exists)
|
||||
.and_then(sqlgen::read<std::vector<Person>>)
|
||||
.value();
|
||||
|
||||
const std::string expected = R"([])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(people), expected);
|
||||
}
|
||||
|
||||
} // namespace test_create_table
|
||||
|
||||
#endif
|
||||
@@ -13,7 +13,9 @@ struct TestTable {
|
||||
};
|
||||
|
||||
TEST(postgres, test_create_table_dry) {
|
||||
const auto query = sqlgen::CreateTable<TestTable>{};
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto query = create_table<TestTable> | if_not_exists;
|
||||
|
||||
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"));)";
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_create_index {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_create_index) {
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto people = sqlgen::sqlite::connect()
|
||||
.and_then(create_table<Person> | if_not_exists)
|
||||
.and_then(create_index<"person_ix", Person>(
|
||||
"first_name"_c, "last_name"_c) |
|
||||
if_not_exists)
|
||||
.and_then(sqlgen::read<std::vector<Person>>);
|
||||
|
||||
const std::string expected = R"([])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(people), expected);
|
||||
}
|
||||
|
||||
} // namespace test_create_index
|
||||
Reference in New Issue
Block a user