summaryrefslogtreecommitdiff
path: root/lib/input
diff options
context:
space:
mode:
Diffstat (limited to 'lib/input')
-rw-r--r--lib/input/mysqlBindings.h178
-rw-r--r--lib/input/mysqlConn.cpp73
-rw-r--r--lib/input/mysqlConn.h3
-rw-r--r--lib/input/mysqlRecordSet.cpp99
-rw-r--r--lib/input/mysqlRecordSet.h37
-rw-r--r--lib/input/mysqlStmt.cpp35
-rw-r--r--lib/input/mysqlStmt.h29
7 files changed, 391 insertions, 63 deletions
diff --git a/lib/input/mysqlBindings.h b/lib/input/mysqlBindings.h
new file mode 100644
index 0000000..dcb3ebf
--- /dev/null
+++ b/lib/input/mysqlBindings.h
@@ -0,0 +1,178 @@
+#ifndef MYGRATE_INPUT_MYSQLBINDINGS_H
+#define MYGRATE_INPUT_MYSQLBINDINGS_H
+
+#include <dbTypes.h>
+#include <helpers.h>
+#include <initializer_list>
+#include <mysql.h>
+#include <mysql_types.h>
+#include <variant>
+#include <vector>
+
+namespace MyGrate::Input {
+ struct BingingData {
+ explicit BingingData(unsigned long l, my_bool n = 0) : len {l}, null {n} { }
+ unsigned long len;
+ my_bool null;
+ };
+
+ struct Bindings {
+ // NOLINTNEXTLINE(hicpp-explicit-conversions)
+ explicit Bindings(const std::initializer_list<DbValue> & vs)
+ {
+ binds.reserve(vs.size());
+ data.reserve(vs.size());
+ for (const auto & v : vs) {
+ std::visit(*this, v);
+ }
+ }
+ template<std::integral T>
+ void
+ operator()(const T & v)
+ {
+ auto & b = binds.emplace_back();
+ b.buffer_type = MySQL::CType<T>::type;
+ b.buffer = const_cast<T *>(&v);
+ b.is_unsigned = std::unsigned_integral<T>;
+ }
+ template<std::floating_point T>
+ void
+ operator()(const T & v)
+ {
+ auto & b = binds.emplace_back();
+ b.buffer_type = MySQL::CType<T>::type;
+ b.buffer = const_cast<T *>(&v);
+ }
+ template<Viewable T>
+ void
+ operator()(const T & v)
+ {
+ auto & b = binds.emplace_back();
+ b.buffer_type = MySQL::CType<T>::type;
+ b.buffer = const_cast<typename T::value_type *>(v.data());
+ b.length = &data.emplace_back(v.size(), 0).len;
+ }
+ void
+ operator()(const std::nullptr_t &)
+ {
+ auto & b = binds.emplace_back();
+ b.buffer = nullptr;
+ b.is_null = &data.emplace_back(0, 1).null;
+ }
+ template<typename T>
+ void
+ operator()(const T &)
+ {
+ throw std::runtime_error("Not implemented");
+ }
+ std::vector<MYSQL_BIND> binds;
+ std::vector<BingingData> data;
+ };
+
+ class ResultData : public BingingData {
+ public:
+ ResultData() : BingingData {0} { }
+ virtual ~ResultData() = default;
+
+ [[nodiscard]] virtual DbValue getValue() const = 0;
+ };
+
+ template<typename T> class ResultDataT : public ResultData {
+ public:
+ ResultDataT(MYSQL_BIND & b, const MYSQL_FIELD & f)
+ {
+ b.buffer = &buf;
+ b.buffer_length = sizeof(T);
+ b.is_null = &this->null;
+ b.length = &this->len;
+ b.is_unsigned = std::is_unsigned_v<T>;
+ b.buffer_type = f.type;
+ }
+
+ [[nodiscard]] DbValue
+ getValue() const override
+ {
+ return buf;
+ }
+
+ private:
+ T buf {};
+ };
+
+ template<> class ResultDataT<std::string_view> : public ResultData {
+ public:
+ ResultDataT(MYSQL_BIND & b, const MYSQL_FIELD & f) : buf(f.length)
+ {
+ b.buffer_length = buf.size();
+ b.buffer = buf.data();
+ b.is_null = &this->null;
+ b.length = &this->len;
+ b.buffer_type = f.type;
+ }
+
+ [[nodiscard]] DbValue
+ getValue() const override
+ {
+ return std::string_view {buf.data(), this->len};
+ }
+
+ private:
+ std::vector<char> buf;
+ };
+
+ template<> class ResultDataT<Blob> : public ResultData {
+ public:
+ ResultDataT(MYSQL_BIND & b, const MYSQL_FIELD & f) : buf(f.length)
+ {
+ b.buffer_length = buf.size();
+ b.buffer = buf.data();
+ b.is_null = &this->null;
+ b.length = &this->len;
+ b.buffer_type = f.type;
+ }
+
+ [[nodiscard]] DbValue
+ getValue() const override
+ {
+ return Blob {buf.data(), this->len};
+ }
+
+ private:
+ std::vector<std::byte> buf;
+ };
+
+ template<typename Out> class ResultDataTime : public ResultData {
+ public:
+ ResultDataTime(MYSQL_BIND & b, const MYSQL_FIELD & f)
+ {
+ b.buffer_length = sizeof(MYSQL_TIME);
+ b.buffer = &buf;
+ b.is_null = &this->null;
+ b.length = &this->len;
+ b.buffer_type = f.type;
+ }
+
+ [[nodiscard]] DbValue
+ getValue() const override
+ {
+ return Out {*this};
+ }
+
+ private:
+ operator Date() const
+ {
+ return Date(buf.year, buf.month, buf.day);
+ }
+ operator Time() const
+ {
+ return Time(buf.hour, buf.minute, buf.second);
+ }
+ operator DateTime() const
+ {
+ return DateTime(*this, *this);
+ }
+ MYSQL_TIME buf;
+ };
+}
+
+#endif
diff --git a/lib/input/mysqlConn.cpp b/lib/input/mysqlConn.cpp
index 179f9d5..46ed7c6 100644
--- a/lib/input/mysqlConn.cpp
+++ b/lib/input/mysqlConn.cpp
@@ -1,18 +1,17 @@
#include "mysqlConn.h"
-#include "helpers.h"
+#include "mysqlBindings.h"
+#include "mysqlStmt.h"
#include <cstddef>
#include <cstring>
+#include <dbConn.h>
#include <dbTypes.h>
+#include <helpers.h>
#include <memory>
#include <mysql.h>
-#include <mysql_types.h>
#include <stdexcept>
-#include <variant>
#include <vector>
namespace MyGrate::Input {
- using StmtPtr = std::unique_ptr<MYSQL_STMT, decltype(&mysql_stmt_close)>;
-
MySQLConn::MySQLConn(
const char * const host, const char * const user, const char * const pass, unsigned short port) :
st_mysql {}
@@ -36,64 +35,6 @@ namespace MyGrate::Input {
verify<std::runtime_error>(!mysql_query(this, q), q);
}
- struct Bindings {
- // NOLINTNEXTLINE(hicpp-explicit-conversions)
- explicit Bindings(const std::initializer_list<DbValue> & vs)
- {
- binds.reserve(vs.size());
- extras.reserve(vs.size());
- for (const auto & v : vs) {
- std::visit(*this, v);
- }
- }
- template<std::integral T>
- void
- operator()(const T & v)
- {
- auto & b = binds.emplace_back();
- b.buffer_type = MySQL::CType<T>::type;
- b.buffer = const_cast<T *>(&v);
- b.is_unsigned = std::unsigned_integral<T>;
- }
- template<std::floating_point T>
- void
- operator()(const T & v)
- {
- auto & b = binds.emplace_back();
- b.buffer_type = MySQL::CType<T>::type;
- b.buffer = const_cast<T *>(&v);
- }
- template<Viewable T>
- void
- operator()(const T & v)
- {
- auto & b = binds.emplace_back();
- b.buffer_type = MySQL::CType<T>::type;
- b.buffer = const_cast<typename T::value_type *>(v.data());
- b.length = &extras.emplace_back(v.size(), 0).len;
- }
- void
- operator()(const std::nullptr_t &)
- {
- auto & b = binds.emplace_back();
- b.buffer = nullptr;
- b.is_null = &extras.emplace_back(0, 1).null;
- }
- template<typename T>
- void
- operator()(const T &)
- {
- throw std::runtime_error("Not implemented");
- }
- struct extra {
- explicit extra(unsigned long l, my_bool n = 0) : len {l}, null {n} { }
- unsigned long len;
- my_bool null;
- };
- std::vector<MYSQL_BIND> binds;
- std::vector<extra> extras;
- };
-
void
MySQLConn::query(const char * const q, const std::initializer_list<DbValue> & vs)
{
@@ -104,4 +45,10 @@ namespace MyGrate::Input {
verify<std::runtime_error>(!mysql_stmt_bind_param(stmt.get(), b.binds.data()), "Param count mismatch");
verify<std::runtime_error>(!mysql_stmt_execute(stmt.get()), q);
}
+
+ DbPrepStmtPtr
+ MySQLConn::prepare(const char * const q, std::size_t)
+ {
+ return std::make_unique<MySQLPrepStmt>(q, this);
+ }
}
diff --git a/lib/input/mysqlConn.h b/lib/input/mysqlConn.h
index 9e4ec25..2f71262 100644
--- a/lib/input/mysqlConn.h
+++ b/lib/input/mysqlConn.h
@@ -1,6 +1,7 @@
#ifndef MYGRATE_INPUT_MYSQLCONN_H
#define MYGRATE_INPUT_MYSQLCONN_H
+#include <cstddef>
#include <dbConn.h>
#include <dbTypes.h>
#include <initializer_list>
@@ -14,6 +15,8 @@ namespace MyGrate::Input {
void query(const char * const) override;
void query(const char * const q, const std::initializer_list<DbValue> &) override;
+
+ DbPrepStmtPtr prepare(const char * const, std::size_t) override;
};
}
diff --git a/lib/input/mysqlRecordSet.cpp b/lib/input/mysqlRecordSet.cpp
new file mode 100644
index 0000000..51be15f
--- /dev/null
+++ b/lib/input/mysqlRecordSet.cpp
@@ -0,0 +1,99 @@
+#include "mysqlRecordSet.h"
+#include "mysqlBindings.h"
+#include "mysqlStmt.h"
+#include <cstdint>
+#include <dbTypes.h>
+#include <helpers.h>
+#include <stdexcept>
+#include <string_view>
+#include <utility>
+// IWYU pragma: no_include <ext/alloc_traits.h>
+
+namespace MyGrate::Input {
+ MySQLRecordSet::MySQLRecordSet(StmtPtr s) :
+ stmt {std::move(s)}, stmtres {nullptr, nullptr}, fields(mysql_stmt_field_count(stmt.get())),
+ extras(fields.size())
+ {
+ auto getBind = [](const MYSQL_FIELD & f, MYSQL_BIND & b) -> std::unique_ptr<ResultData> {
+ switch (f.type) {
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_DOUBLE:
+ return std::make_unique<ResultDataT<double>>(b, f);
+ case MYSQL_TYPE_FLOAT:
+ return std::make_unique<ResultDataT<float>>(b, f);
+ case MYSQL_TYPE_TINY:
+ return std::make_unique<ResultDataT<int8_t>>(b, f);
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ return std::make_unique<ResultDataT<int16_t>>(b, f);
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_INT24:
+ return std::make_unique<ResultDataT<int32_t>>(b, f);
+ case MYSQL_TYPE_LONGLONG:
+ return std::make_unique<ResultDataT<int64_t>>(b, f);
+ case MYSQL_TYPE_NULL:
+ return std::make_unique<ResultDataT<std::nullptr_t>>(b, f);
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP2:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_DATETIME2:
+ return std::make_unique<ResultDataTime<DateTime>>(b, f);
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_TIME2:
+ return std::make_unique<ResultDataTime<Time>>(b, f);
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_NEWDATE:
+ return std::make_unique<ResultDataTime<Date>>(b, f);
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_ENUM:
+ return std::make_unique<ResultDataT<std::string_view>>(b, f);
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ return std::make_unique<ResultDataT<Blob>>(b, f);
+ case MAX_NO_FIELD_TYPES:
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_GEOMETRY:;
+ }
+ throw std::runtime_error("Unsupported column type");
+ };
+ ResPtr meta {mysql_stmt_result_metadata(stmt.get()), mysql_free_result};
+ const auto fieldDefs = mysql_fetch_fields(meta.get());
+ verify<std::runtime_error>(fieldDefs, "Fetch fields");
+ for (std::size_t i = 0; i < fields.size(); i += 1) {
+ extras[i] = getBind(fieldDefs[i], fields[i]);
+ }
+ verify<std::runtime_error>(!mysql_stmt_bind_result(stmt.get(), fields.data()), "Store result error");
+ verify<std::runtime_error>(!mysql_stmt_store_result(stmt.get()), "Store result error");
+ stmtres = {stmt.get(), mysql_stmt_free_result};
+ verify<std::runtime_error>(!mysql_stmt_fetch(stmt.get()), "Fetch");
+ }
+
+ std::size_t
+ MySQLRecordSet::rows() const
+ {
+ return mysql_stmt_num_rows(stmt.get());
+ }
+
+ std::size_t
+ MySQLRecordSet::columns() const
+ {
+ return fields.size();
+ }
+
+ DbValue
+ MySQLRecordSet::at(std::size_t row, std::size_t col) const
+ {
+ mysql_stmt_data_seek(stmt.get(), row);
+ if (extras[col]->null) {
+ return nullptr;
+ }
+ return extras[col]->getValue();
+ }
+}
diff --git a/lib/input/mysqlRecordSet.h b/lib/input/mysqlRecordSet.h
new file mode 100644
index 0000000..849a653
--- /dev/null
+++ b/lib/input/mysqlRecordSet.h
@@ -0,0 +1,37 @@
+#ifndef MYGRATE_INPUT_MYSQLRECORDSET_H
+#define MYGRATE_INPUT_MYSQLRECORDSET_H
+
+#include "mysqlStmt.h"
+#include <cstddef>
+#include <dbRecordSet.h>
+#include <dbTypes.h>
+#include <memory>
+#include <mysql.h>
+#include <vector>
+
+namespace MyGrate::Input {
+ class ResultData;
+
+ class MySQLRecordSet : public RecordSet {
+ public:
+ using ResPtr = std::unique_ptr<MYSQL_RES, decltype(&mysql_free_result)>;
+ using StmtResPtr = std::unique_ptr<MYSQL_STMT, decltype(&mysql_stmt_free_result)>;
+ using ResultDataPtr = std::unique_ptr<ResultData>;
+
+ explicit MySQLRecordSet(StmtPtr s);
+
+ std::size_t rows() const override;
+
+ std::size_t columns() const override;
+
+ DbValue at(std::size_t row, std::size_t col) const override;
+
+ private:
+ StmtPtr stmt;
+ StmtResPtr stmtres;
+ std::vector<MYSQL_BIND> fields;
+ std::vector<ResultDataPtr> extras;
+ };
+}
+
+#endif
diff --git a/lib/input/mysqlStmt.cpp b/lib/input/mysqlStmt.cpp
new file mode 100644
index 0000000..08d1303
--- /dev/null
+++ b/lib/input/mysqlStmt.cpp
@@ -0,0 +1,35 @@
+#include "mysqlStmt.h"
+#include "mysqlBindings.h"
+#include "mysqlRecordSet.h"
+#include <cstring>
+#include <helpers.h>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+namespace MyGrate::Input {
+ MySQLPrepStmt::MySQLPrepStmt(const char * const q, MYSQL * c) : stmt {mysql_stmt_init(c), &mysql_stmt_close}
+ {
+ verify<std::runtime_error>(!mysql_stmt_prepare(stmt.get(), q, strlen(q)), q);
+ }
+
+ void
+ MySQLPrepStmt::execute(const std::initializer_list<DbValue> & vs)
+ {
+ Bindings b {vs};
+ verify<std::runtime_error>(!mysql_stmt_bind_param(stmt.get(), b.binds.data()), "Param count mismatch");
+ verify<std::runtime_error>(!mysql_stmt_execute(stmt.get()), "Prepared statement execute");
+ }
+
+ std::size_t
+ MySQLPrepStmt::rows() const
+ {
+ return mysql_stmt_affected_rows(stmt.get());
+ }
+
+ RecordSetPtr
+ MySQLPrepStmt::recordSet()
+ {
+ return std::make_unique<MySQLRecordSet>(std::move(stmt));
+ }
+}
diff --git a/lib/input/mysqlStmt.h b/lib/input/mysqlStmt.h
new file mode 100644
index 0000000..a42e0db
--- /dev/null
+++ b/lib/input/mysqlStmt.h
@@ -0,0 +1,29 @@
+#ifndef MYGRATE_INPUT_MYSQLSTMT_H
+#define MYGRATE_INPUT_MYSQLSTMT_H
+
+#include "dbConn.h"
+#include "dbRecordSet.h"
+#include "dbTypes.h"
+#include <cstddef>
+#include <initializer_list>
+#include <memory>
+#include <mysql.h>
+
+namespace MyGrate::Input {
+ using StmtPtr = std::unique_ptr<MYSQL_STMT, decltype(&mysql_stmt_close)>;
+
+ class MySQLPrepStmt : public DbPrepStmt {
+ public:
+ MySQLPrepStmt(const char * const q, MYSQL * c);
+ void execute(const std::initializer_list<DbValue> & vs) override;
+
+ std::size_t rows() const override;
+
+ RecordSetPtr recordSet() override;
+
+ private:
+ StmtPtr stmt;
+ };
+}
+
+#endif