Added support for MySQL (#33); resolves #18

This commit is contained in:
Dr. Patrick Urbanke (劉自成)
2025-07-27 12:25:12 +02:00
committed by GitHub
parent 1424d29c5d
commit 3f8c5556d2
81 changed files with 4338 additions and 23 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ using Type =
rfl::TaggedUnion<"type", types::Unknown, types::Boolean, types::Float32,
types::Float64, types::Int8, types::Int16, types::Int32,
types::Int64, types::UInt8, types::UInt16, types::UInt32,
types::UInt64, types::Text, types::Timestamp,
types::UInt64, types::Text, types::Date, types::Timestamp,
types::TimestampWithTZ, types::VarChar>;
} // namespace sqlgen::dynamic
+5
View File
@@ -65,6 +65,11 @@ struct Text {
Properties properties;
};
struct Date {
std::string tz;
Properties properties;
};
struct Timestamp {
std::string tz;
Properties properties;
@@ -23,6 +23,15 @@ std::string replace_all(const std::string& _str, const std::string& _from,
std::vector<std::string> split(const std::string& _str,
const std::string& _delimiter);
std::string ltrim(const std::string& _str, const std::string& _chars = " ");
std::string rtrim(const std::string& _str, const std::string& _chars = " ");
inline std::string trim(const std::string& _str,
const std::string& _chars = " ") {
return ltrim(rtrim(_str, _chars), _chars);
}
} // namespace sqlgen::internal::strings
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef SQLGEN_MYSQL_HPP_
#define SQLGEN_MYSQL_HPP_
#include "../sqlgen.hpp"
#include "mysql/Credentials.hpp"
#include "mysql/connect.hpp"
#include "mysql/to_sql.hpp"
#endif
+97
View File
@@ -0,0 +1,97 @@
#ifndef SQLGEN_MYSQL_CONNECTION_HPP_
#define SQLGEN_MYSQL_CONNECTION_HPP_
#include <mysql.h>
#include <memory>
#include <rfl.hpp>
#include <stdexcept>
#include <string>
#include "../IteratorBase.hpp"
#include "../Ref.hpp"
#include "../Result.hpp"
#include "../Transaction.hpp"
#include "../dynamic/Column.hpp"
#include "../dynamic/Statement.hpp"
#include "../dynamic/Write.hpp"
#include "../is_connection.hpp"
#include "Credentials.hpp"
#include "exec.hpp"
#include "to_sql.hpp"
namespace sqlgen::mysql {
class Connection {
using ConnPtr = Ref<MYSQL>;
using StmtPtr = std::shared_ptr<MYSQL_STMT>;
public:
Connection(const Credentials& _credentials)
: conn_(make_conn(_credentials)) {}
static rfl::Result<Ref<Connection>> make(
const Credentials& _credentials) noexcept;
~Connection() = default;
Result<Nothing> begin_transaction() noexcept {
return execute("START TRANSACTION;");
}
Result<Nothing> commit() noexcept { return execute("COMMIT;"); }
Result<Nothing> execute(const std::string& _sql) noexcept {
return exec(conn_, _sql);
}
Result<Nothing> insert(
const dynamic::Insert& _stmt,
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept;
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query);
Result<Nothing> rollback() noexcept { return execute("ROLLBACK;"); }
std::string to_sql(const dynamic::Statement& _stmt) noexcept {
return to_sql_impl(_stmt);
}
Result<Nothing> start_write(const dynamic::Write& _stmt);
Result<Nothing> write(
const std::vector<std::vector<std::optional<std::string>>>& _data);
Result<Nothing> end_write();
private:
/// Actually inserts data based on a prepared statement -
/// used by both .insert(...) and .write(...).
Result<Nothing> actual_insert(
const std::vector<std::vector<std::optional<std::string>>>& _data,
MYSQL_STMT* _stmt) const noexcept;
static ConnPtr make_conn(const Credentials& _credentials);
Result<StmtPtr> prepare_statement(
const std::variant<dynamic::Insert, dynamic::Write>& _stmt)
const noexcept;
private:
/// A prepared statement - needed for the read and write operations. Note that
/// we have declared it before conn_, meaning it will be destroyed first.
StmtPtr stmt_;
/// The underlying connection.
ConnPtr conn_;
};
static_assert(is_connection<Connection>,
"Must fulfill the is_connection concept.");
static_assert(is_connection<Transaction<Connection>>,
"Must fulfill the is_connection concept.");
} // namespace sqlgen::mysql
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef SQLGEN_MYSQL_CREDENTIALS_HPP_
#define SQLGEN_MYSQL_CREDENTIALS_HPP_
#include <string>
namespace sqlgen::mysql {
struct Credentials {
std::string host;
std::string user;
std::string password;
std::string dbname = "mysql";
int port = 3306;
std::string unix_socket = "/var/run/mysqld/mysqld.sock";
};
} // namespace sqlgen::mysql
#endif
+50
View File
@@ -0,0 +1,50 @@
#ifndef SQLGEN_MYSQL_ITERATOR_HPP_
#define SQLGEN_MYSQL_ITERATOR_HPP_
#include <mysql.h>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "../IteratorBase.hpp"
#include "../Ref.hpp"
#include "../Result.hpp"
#include "Connection.hpp"
namespace sqlgen::mysql {
class Iterator : public sqlgen::IteratorBase {
using ConnPtr = Ref<MYSQL>;
using ResPtr = Ref<MYSQL_RES>;
public:
Iterator(const ResPtr& _res, const ConnPtr& _conn);
~Iterator();
/// Whether the end of the available data has been reached.
bool end() const final { return end_; }
/// Returns the next batch of rows.
/// If _batch_size is greater than the number of rows left, returns all
/// of the rows left.
Result<std::vector<std::vector<std::optional<std::string>>>> next(
const size_t _batch_size) final;
private:
/// The underlying mysql result.
ResPtr res_;
/// The underlying mysql connection. We have this in here to prevent its
/// destruction for the lifetime of the iterator.
ConnPtr conn_;
/// Whether the end is reached.
bool end_;
};
} // namespace sqlgen::mysql
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef SQLGEN_MYSQL_CONNECT_HPP_
#define SQLGEN_MYSQL_CONNECT_HPP_
#include <string>
#include "Connection.hpp"
#include "Credentials.hpp"
namespace sqlgen::mysql {
inline auto connect(const Credentials& _credentials) {
return Connection::make(_credentials);
}
} // namespace sqlgen::mysql
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef SQLGEN_MYSQL_EXEC_HPP_
#define SQLGEN_MYSQL_EXEC_HPP_
#include <mysql.h>
#include <string>
#include "../Ref.hpp"
#include "../Result.hpp"
namespace sqlgen::mysql {
Result<Nothing> exec(const Ref<MYSQL>& _conn, const std::string& _sql) noexcept;
} // namespace sqlgen::mysql
#endif
+22
View File
@@ -0,0 +1,22 @@
#ifndef SQLGEN_MYSQL_MAKE_ERROR_HPP_
#define SQLGEN_MYSQL_MAKE_ERROR_HPP_
#include <mysql.h>
#include <rfl.hpp>
#include <string>
#include "../Ref.hpp"
#include "../Result.hpp"
namespace sqlgen::mysql {
inline rfl::Unexpected<Error> make_error(const Ref<MYSQL>& _conn) noexcept {
return error("MySQL error (" + std::to_string(mysql_errno(_conn.get())) +
") [" + mysql_sqlstate(_conn.get()) + "] " +
mysql_error(_conn.get()));
}
} // namespace sqlgen::mysql
#endif
+27
View File
@@ -0,0 +1,27 @@
#ifndef SQLGEN_MYSQL_TO_SQL_HPP_
#define SQLGEN_MYSQL_TO_SQL_HPP_
#include <string>
#include <type_traits>
#include "../dynamic/Statement.hpp"
#include "../transpilation/to_sql.hpp"
namespace sqlgen::mysql {
/// Transpiles a dynamic general SQL statement to the mysql dialect.
std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept;
/// Transpiles any SQL statement to the mysql 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::mysql
#endif
+8 -2
View File
@@ -29,10 +29,16 @@ struct Parser<Timestamp<_format>> {
static dynamic::Type to_type() noexcept {
const std::string format = typename TSType::Format().str();
if (format.find("%z") == std::string::npos) {
if (format.find("%z") != std::string::npos) {
return dynamic::types::TimestampWithTZ{};
} else if (format.find("%H") != std::string::npos ||
format.find("%M") != std::string::npos ||
format.find("%S") != std::string::npos ||
format.find("%R") != std::string::npos ||
format.find("%T") != std::string::npos) {
return dynamic::types::Timestamp{};
} else {
return dynamic::types::TimestampWithTZ{};
return dynamic::types::Date{};
}
}
};