why
parent
af2295963e
commit
474986c021
|
@ -0,0 +1,5 @@
|
|||
cmake-build-*/
|
||||
.env
|
||||
*.env
|
||||
*.sqlite3
|
||||
*.sqlite
|
112
include/sql.h
112
include/sql.h
|
@ -24,14 +24,16 @@
|
|||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <blt/iterator/enumerate.h>
|
||||
#include <blt/logging/logging.h>
|
||||
#include <blt/std/utility.h>
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename T>
|
||||
std::string_view sql_name()
|
||||
std::string sql_name()
|
||||
{
|
||||
using Decay = std::decay_t<T>
|
||||
using Decay = std::decay_t<T>;
|
||||
if constexpr (std::is_same_v<Decay, nullptr_t>)
|
||||
return "NULL";
|
||||
else if constexpr (std::is_integral_v<Decay>)
|
||||
|
@ -45,6 +47,13 @@ namespace detail
|
|||
else
|
||||
return "BLOB";
|
||||
}
|
||||
|
||||
struct foreign_key_t
|
||||
{
|
||||
std::string local_name;
|
||||
std::string foreign_table;
|
||||
std::string foreign_name;
|
||||
};
|
||||
}
|
||||
|
||||
class column_t
|
||||
|
@ -94,7 +103,7 @@ public:
|
|||
|
||||
private:
|
||||
template <typename... Types, size_t... Indices>
|
||||
[[nodiscard]] std::tuple<Types...> get_internal(std::index_sequence<Indices>) const
|
||||
[[nodiscard]] std::tuple<Types...> get_internal(std::index_sequence<Indices...>) const
|
||||
{
|
||||
return std::tuple<Types...>{get<Types>(Indices)...};
|
||||
}
|
||||
|
@ -109,10 +118,10 @@ public:
|
|||
{}
|
||||
|
||||
template <typename T>
|
||||
auto bind(T&& type, int col)
|
||||
int bind(const T& type, int col)
|
||||
{
|
||||
static_assert(!std::is_rvalue_reference_v<T>, "Lifetime of object must outlive its usage!");
|
||||
using Decay = std::decay<T>;
|
||||
using Decay = std::decay_t<T>;
|
||||
if constexpr (std::is_same_v<Decay, bool> || std::is_integral_v<Decay>)
|
||||
{
|
||||
if constexpr (sizeof(Decay) == 8)
|
||||
|
@ -128,9 +137,12 @@ public:
|
|||
} else if constexpr (std::is_same_v<Decay, std::string> || std::is_same_v<Decay, std::string_view>)
|
||||
{
|
||||
return sqlite3_bind_text(statement, col, type.data(), type.size(), nullptr);
|
||||
} else if constexpr (std::is_same_v<Decay, char*> || std::is_same_v<Decay, const char*>)
|
||||
{
|
||||
return sqlite3_bind_text(statement, col, type, -1, nullptr);
|
||||
} else
|
||||
{
|
||||
return sqlite3_bind_blob64(statement, col, type.data(), type.size());
|
||||
return sqlite3_bind_blob64(statement, col, type.data(), type.data(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,29 +160,29 @@ public:
|
|||
* Indexes start at 1 for the left most template parameter
|
||||
*/
|
||||
template <typename T>
|
||||
auto bind(T&& type, int col)
|
||||
int bind(const T& type, int col)
|
||||
{
|
||||
return column_binder_t{statement}.bind(std::forward<T>(type), col);
|
||||
return column_binder_t{statement}.bind(type, col);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto bind(T&& type, const std::string& name)
|
||||
int bind(const T& type, const std::string& name)
|
||||
{
|
||||
auto index = sqlite3_bind_parameter_index(statement, name.c_str());
|
||||
return column_binder_t{statement}.bind(std::forward<T>(type), index);
|
||||
return column_binder_t{statement}.bind(type, index);
|
||||
}
|
||||
|
||||
template <typename... Types>
|
||||
auto bind(Types&& types)
|
||||
int bind_all(const Types&... types)
|
||||
{
|
||||
return bind_internal<Types...>(std::index_sequence_for<Types...>(), std::forward<Types>(types)...);
|
||||
return bind_internal<Types...>(std::index_sequence_for<Types...>(), types...);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... Types, size_t Indices>
|
||||
auto bind_internal(std::index_sequence<Indices...>, Types&& types)
|
||||
template <typename... Types, size_t... Indices>
|
||||
int bind_internal(std::index_sequence<Indices...>, const Types&... types)
|
||||
{
|
||||
return ((bind<Types>(Indices, std::forward<Types>(types)) != SQLITE_OK) || ...);
|
||||
return ((bind<Types>(types, Indices + 1) != SQLITE_OK) | ...);
|
||||
}
|
||||
|
||||
sqlite3_stmt* statement;
|
||||
|
@ -195,13 +207,13 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
binder_t bind() const
|
||||
binder_t bind() const // NOLINT
|
||||
{
|
||||
sqlite3_reset(statement);
|
||||
return binder_t{statement};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool execute() const;
|
||||
[[jetbrains::has_side_effects]] bool execute() const; // NOLINT
|
||||
|
||||
[[nodiscard]] column_t fetch() const
|
||||
{
|
||||
|
@ -220,15 +232,17 @@ class table_builder_t;
|
|||
class table_column_builder_t
|
||||
{
|
||||
public:
|
||||
table_column_builder_t(table_builder_t& parent, std::vector<std::string>& columns, std::string type, std::string name): parent{parent},
|
||||
columns{columns},
|
||||
table_column_builder_t(table_builder_t& parent, std::vector<std::string>& columns, std::vector<std::string>& primary_keys,
|
||||
std::vector<detail::foreign_key_t>& foreign_keys, std::string type, std::string name): parent{parent}, columns{columns},
|
||||
primary_keys{primary_keys},
|
||||
foreign_keys{foreign_keys},
|
||||
type{std::move(type)},
|
||||
name{std::move(name)}
|
||||
{}
|
||||
|
||||
table_column_builder_t& primary_key()
|
||||
{
|
||||
attributes.emplace_back("PRIMARY KEY");
|
||||
primary_keys.push_back(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -253,6 +267,12 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
table_column_builder_t& foreign_key(const std::string& table, const std::string& column)
|
||||
{
|
||||
foreign_keys.emplace_back(detail::foreign_key_t{name, table, column});
|
||||
return *this;
|
||||
}
|
||||
|
||||
table_builder_t& finish()
|
||||
{
|
||||
if (!created)
|
||||
|
@ -281,8 +301,10 @@ private:
|
|||
}
|
||||
|
||||
bool created = false;
|
||||
table_builder_t& parent
|
||||
table_builder_t& parent;
|
||||
std::vector<std::string>& columns;
|
||||
std::vector<std::string>& primary_keys;
|
||||
std::vector<detail::foreign_key_t>& foreign_keys;
|
||||
std::vector<std::string> attributes;
|
||||
std::string type;
|
||||
std::string name;
|
||||
|
@ -291,17 +313,54 @@ private:
|
|||
class table_builder_t
|
||||
{
|
||||
public:
|
||||
template <typename>
|
||||
struct required_string_t
|
||||
{
|
||||
std::string name;
|
||||
|
||||
required_string_t(std::string name): name{std::move(name)} // NOLINT
|
||||
{}
|
||||
|
||||
operator std::string() // NOLINT
|
||||
{
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
explicit table_builder_t(sqlite3* db, std::string name): db{db}, name{std::move(name)}
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
table_column_builder_t& with_type(const std::string& name)
|
||||
table_column_builder_t with_column(const std::string& name)
|
||||
{
|
||||
return table_column_builder_t{*this, columns, detail::sql_name<T>(), name};
|
||||
return table_column_builder_t{*this, columns, primary_keys, foreign_keys, detail::sql_name<T>(), name};
|
||||
}
|
||||
|
||||
template <typename... Types>
|
||||
table_builder_t& with_columns(required_string_t<Types>... names)
|
||||
{
|
||||
(with_column<Types>(names.name), ...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
table_builder_t& with_foreign_key(const std::string& local_name, const std::string& foreign_table, const std::string& foreign_name)
|
||||
{
|
||||
foreign_keys.emplace_back(detail::foreign_key_t{local_name, foreign_table, foreign_name});
|
||||
return *this;
|
||||
}
|
||||
|
||||
table_builder_t& with_primary_key(const std::string& name)
|
||||
{
|
||||
primary_keys.push_back(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
statement_t build();
|
||||
|
||||
private:
|
||||
std::vector<std::string> columns{};
|
||||
std::vector<std::string> primary_keys{};
|
||||
std::vector<detail::foreign_key_t> foreign_keys{};
|
||||
sqlite3* db;
|
||||
std::string name{};
|
||||
};
|
||||
|
@ -312,7 +371,7 @@ public:
|
|||
explicit statement_builder_t(sqlite3* db): db{db}
|
||||
{}
|
||||
|
||||
table_builder_t create_table(const std::string& name)
|
||||
table_builder_t create_table(const std::string& name) const
|
||||
{
|
||||
return table_builder_t{db, name};
|
||||
}
|
||||
|
@ -349,6 +408,11 @@ public:
|
|||
return statement_builder_t{db};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_error() const
|
||||
{
|
||||
return sqlite3_errmsg(db);
|
||||
}
|
||||
|
||||
~database_t();
|
||||
|
||||
private:
|
||||
|
|
23
src/main.cpp
23
src/main.cpp
|
@ -19,6 +19,7 @@
|
|||
#include "blt/gfx/renderer/batch_2d_renderer.h"
|
||||
#include "blt/gfx/renderer/camera.h"
|
||||
#include <imgui.h>
|
||||
#include <sql.h>
|
||||
|
||||
blt::gfx::matrix_state_manager global_matrices;
|
||||
blt::gfx::resource_manager resources;
|
||||
|
@ -56,5 +57,25 @@ void destroy(const blt::gfx::window_data&)
|
|||
|
||||
int main()
|
||||
{
|
||||
blt::gfx::init(blt::gfx::window_data{"Minecraft Color Picker", init, update, destroy}.setSyncInterval(1));
|
||||
// blt::gfx::init(blt::gfx::window_data{"Minecraft Color Picker", init, update, destroy}.setSyncInterval(1));
|
||||
database_t db("test.db");
|
||||
auto silly_table = db.builder().create_table("silly");
|
||||
silly_table.with_column<float>("meep").not_null().primary_key();
|
||||
silly_table.with_column<int>("merow").unique();
|
||||
silly_table.with_column<std::string>("meow").primary_key();
|
||||
const auto silly_table_statement = silly_table.build();
|
||||
silly_table_statement.execute();
|
||||
|
||||
const auto sql = "INSERT INTO silly (meep, merow, meow) VALUES (?, ?, ?)";
|
||||
const auto insert_statement = db.prepare(sql);
|
||||
static auto meow2 = "meow1";
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
auto str = meow2 + std::to_string(i);
|
||||
auto values = insert_statement.bind();
|
||||
values.bind_all(0.5, i, str);
|
||||
insert_statement.execute();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
37
src/sql.cpp
37
src/sql.cpp
|
@ -26,7 +26,8 @@ statement_t::statement_t(sqlite3* db, const std::string& stmt): db{db}
|
|||
|
||||
bool statement_t::execute() const
|
||||
{
|
||||
return sqlite3_step(statement) == SQLITE_OK;
|
||||
const auto v = sqlite3_step(statement);
|
||||
return v == SQLITE_OK || v == SQLITE_DONE;
|
||||
}
|
||||
|
||||
statement_t::~statement_t()
|
||||
|
@ -34,6 +35,40 @@ statement_t::~statement_t()
|
|||
sqlite3_finalize(statement);
|
||||
}
|
||||
|
||||
statement_t table_builder_t::build()
|
||||
{
|
||||
std::string sql = "CREATE TABLE IF NOT EXISTS ";
|
||||
sql += name;
|
||||
sql += " (";
|
||||
for (const auto& [i, column] : blt::enumerate(columns))
|
||||
{
|
||||
sql += column;
|
||||
if (i != columns.size() - 1)
|
||||
sql += ", ";
|
||||
}
|
||||
if (!primary_keys.empty())
|
||||
{
|
||||
sql += ", PRIMARY KEY (";
|
||||
for (const auto& [i, key] : blt::enumerate(primary_keys))
|
||||
{
|
||||
sql += key;
|
||||
if (i != primary_keys.size() - 1)
|
||||
sql += ", ";
|
||||
}
|
||||
sql += ")";
|
||||
}
|
||||
if (!foreign_keys.empty())
|
||||
{
|
||||
for (const auto& [i, key] : blt::enumerate(foreign_keys))
|
||||
{
|
||||
sql += ", FOREIGN KEY (";
|
||||
sql += key.local_name + " REFERENCES " + key.foreign_table + "(" + key.foreign_name + ")";
|
||||
}
|
||||
}
|
||||
sql += ");";
|
||||
return statement_t{db, sql};
|
||||
}
|
||||
|
||||
database_t::database_t(const std::string& file)
|
||||
{
|
||||
if (sqlite3_open(file.c_str(), &db) != SQLITE_OK)
|
||||
|
|
Loading…
Reference in New Issue