-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
327 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#pragma once | ||
|
||
#include <pfr-orm/definitions.hpp> | ||
#include <pfr-orm/sqlite/utils.hpp> | ||
|
||
namespace pfrorm::sqlite::detail { | ||
|
||
void createTable(Connection &connection, const EntityDescription &entity); | ||
|
||
bool exists(Connection &connection, const EntityDescription &entity); | ||
|
||
} // namespace pfrorm::sqlite::detail |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#pragma once | ||
|
||
#include <pfr-orm/definitions.hpp> | ||
#include <pfr-orm/sqlite/detail/operations.hpp> | ||
#include <pfr-orm/sqlite/utils.hpp> | ||
|
||
namespace pfrorm::sqlite { | ||
|
||
template <DatabaseEntity T> void createTable(Connection &connection) { | ||
return detail::createTable(connection, DatabaseEntityDescription<T>); | ||
} | ||
|
||
template <typename T> bool exists(Connection &connection) { | ||
return detail::exists(connection, DatabaseEntityDescription<T>); | ||
} | ||
|
||
} // namespace pfrorm::sqlite |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,96 +1,87 @@ | ||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <string> | ||
#include <filesystem> | ||
#include <memory> | ||
#include <optional> | ||
#include <stdexcept> | ||
#include <string_view> | ||
|
||
struct pg_conn; | ||
struct pg_result; | ||
struct sqlite3; | ||
struct sqlite3_stmt; | ||
|
||
namespace pfrorm::postgres { | ||
namespace pfrorm::sqlite { | ||
|
||
class Str { | ||
class Row { | ||
public: | ||
Str(char *str) : str(str) {} | ||
~Str(); | ||
class InvalidRowError : public std::out_of_range { | ||
public: | ||
using std::out_of_range::out_of_range; | ||
}; | ||
|
||
[[nodiscard]] int getColumnCount() const { return this->columnCount; } | ||
|
||
/// @throws InvalidRowError if the column is outside of the range | ||
[[nodiscard]] std::string_view text(int column) const; | ||
|
||
[[nodiscard]] std::string_view view() const { return str; } | ||
operator std::string_view() const { return str; } | ||
/// @throws InvalidRowError if the column is outside of the range | ||
[[nodiscard]] std::int64_t bigint(int column) const; | ||
|
||
Str(const Str &) = delete; | ||
Str(Str &&) = delete; | ||
Str &operator=(const Str &) = delete; | ||
Str &operator=(Str &&) = delete; | ||
/// @throws InvalidRowError if the column is outside of the range | ||
[[nodiscard]] bool boolean(int column) const; | ||
|
||
private: | ||
char *str; | ||
sqlite3_stmt *statement; | ||
|
||
int columnCount; | ||
|
||
friend class Result; | ||
|
||
explicit Row(sqlite3_stmt *const statement, const int columnCount) | ||
: statement(statement), columnCount(columnCount) {} | ||
}; | ||
|
||
class Result { | ||
public: | ||
Result(pg_result *result) : result(result) {} | ||
~Result(); | ||
[[nodiscard]] std::optional<Row> getRow() const { | ||
if (!this->statement.has_value()) { | ||
return std::nullopt; | ||
} | ||
return Row{statement->get(), this->columnCount}; | ||
} | ||
|
||
[[nodiscard]] int status() const; | ||
[[nodiscard]] std::string_view value(int row, int column) const; | ||
bool nextRow(); | ||
|
||
Result(const Result &) = delete; | ||
Result(Result &&) noexcept; | ||
Result &operator=(const Result &) = delete; | ||
Result &operator=(Result &&) = delete; | ||
[[nodiscard]] int getColumnCount() const { return this->columnCount; } | ||
|
||
private: | ||
pg_result *result; | ||
using Statement = std::unique_ptr<sqlite3_stmt, int (*)(sqlite3_stmt *)>; | ||
|
||
std::optional<Statement> statement; | ||
|
||
int columnCount = 0; | ||
|
||
friend class Connection; | ||
|
||
explicit Result(Statement statement); | ||
}; | ||
|
||
class Connection { | ||
public: | ||
Connection(const std::string &connectionStr); | ||
~Connection(); | ||
static Connection fromRaw(sqlite3 *connection); | ||
|
||
Connection(const Connection &) = delete; | ||
Connection(Connection &&) noexcept; | ||
Connection &operator=(const Connection &) = delete; | ||
Connection &operator=(Connection &&) = delete; | ||
static Connection inMemory(const char *name); | ||
|
||
[[nodiscard]] Str escapeIdentifier(std::string_view identifier) const; | ||
static Connection inFile(const std::filesystem::path &path); | ||
|
||
Result execute(const std::string &statement); | ||
void execute(std::string_view statement); | ||
|
||
Result query(const std::string &statement); | ||
Result query(std::string_view statement); | ||
|
||
private: | ||
pg_conn *connection; | ||
}; | ||
|
||
struct ParameterTraits { | ||
struct Parameter { | ||
std::string data; | ||
bool isBinary; // TODO: pass everything as binary | ||
}; | ||
std::unique_ptr<sqlite3, int (*)(sqlite3 *)> connection; | ||
|
||
template <typename T> static Parameter toParam(const T &value) = delete; | ||
explicit Connection(sqlite3 *connection); | ||
}; | ||
|
||
template <> | ||
inline ParameterTraits::Parameter | ||
ParameterTraits::toParam(const uint64_t &value) { | ||
return { | ||
.data = std::to_string(value), | ||
.isBinary = false, | ||
}; | ||
} | ||
|
||
template <> | ||
inline ParameterTraits::Parameter | ||
ParameterTraits::toParam(const std::string &value) { | ||
return { | ||
.data = value, | ||
.isBinary = true, | ||
}; | ||
} | ||
|
||
template <typename T> | ||
concept AsParameter = requires { ParameterTraits::toParam<T>; }; | ||
|
||
} // namespace pfrorm::postgres | ||
} // namespace pfrorm::sqlite |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
#include "../detail/multilambda.hpp" | ||
|
||
#include <pfr-orm/api.hpp> | ||
#include <pfr-orm/definitions.hpp> | ||
#include <pfr-orm/sqlite/detail/operations.hpp> | ||
#include <pfr-orm/sqlite/utils.hpp> | ||
|
||
#include <cstddef> | ||
#include <stdexcept> | ||
#include <string> | ||
#include <string_view> | ||
#include <type_traits> | ||
#include <variant> | ||
#include <vector> | ||
|
||
#include <fmt/core.h> | ||
#include <fmt/format.h> | ||
|
||
namespace pfrorm::sqlite::detail { | ||
|
||
namespace { | ||
|
||
std::string_view toString(const NativeType type) { | ||
switch (type) { | ||
case NativeType::BigInt: | ||
return "INTEGER"; | ||
case NativeType::String: | ||
return "TEXT"; | ||
} | ||
throw std::runtime_error{ | ||
"Unsupported native type " + std::to_string(static_cast<int>(type)), | ||
}; | ||
} | ||
|
||
void createTableFields(const FieldDescription &description, | ||
const bool isPrimaryKey, fmt::appender &appender, | ||
std::vector<std::string_view> prefixes, bool &first) { | ||
prefixes.push_back(description.name); | ||
|
||
const auto createPrimitiveField = | ||
[isPrimaryKey, &prefixes, &appender, | ||
&first](const PrimitiveFieldDescription &descr) { | ||
fmt::format_to(appender, "{}'{}' '{}'{}", first ? "" : ",", | ||
fmt::to_string(fmt::join(prefixes, "_")), | ||
toString(descr.nativeType), | ||
isPrimaryKey ? " PRIMARY KEY" : ""); | ||
first = false; | ||
}; | ||
|
||
const auto createCompositeField = | ||
[&prefixes, &appender, &first](const CompositeFieldDescription &descr) { | ||
for (const FieldDescription &field : descr.fields) { | ||
createTableFields(field, false, appender, prefixes, first); | ||
} | ||
}; | ||
|
||
const pfrorm::detail::MultiLambda createField{ | ||
createPrimitiveField, | ||
createCompositeField, | ||
}; | ||
|
||
static_assert(std::is_invocable_r_v<void, decltype(createField), | ||
const PrimitiveFieldDescription &>); | ||
static_assert(std::is_invocable_r_v<void, decltype(createField), | ||
const CompositeFieldDescription &>); | ||
|
||
std::visit(createField, description.field); | ||
} | ||
|
||
} // namespace | ||
|
||
void createTable(Connection &connection, const EntityDescription &entity) { | ||
connection.execute(fmt::format("DROP TABLE IF EXISTS '{}'", entity.name)); | ||
|
||
fmt::memory_buffer buf; | ||
fmt::appender appender{buf}; | ||
fmt::format_to(appender, "CREATE TABLE '{}' (", entity.name); | ||
bool first = true; | ||
for (std::size_t i = 0; i < entity.fields.size(); ++i) { | ||
createTableFields(entity.fields[i], entity.primaryKey == i, appender, {}, | ||
first); | ||
} | ||
fmt::format_to(appender, ")"); | ||
connection.execute(fmt::to_string(buf)); | ||
} | ||
|
||
bool exists(Connection &connection, const EntityDescription &entity) { | ||
const Result result = connection.query( | ||
fmt::format("SELECT EXISTS(SELECT 1 FROM '{}')", entity.name)); | ||
return result.getRow()->boolean(0); | ||
} | ||
|
||
} // namespace pfrorm::sqlite::detail |
Oops, something went wrong.