mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
This adds a compile time concept to determine if from_chars has floating-point support and uses fallbacks if not. This is a C++17 feature but support for floats was only added to libstdc++ with GCC 11.1 and LLVM libc++ in 20.0 (unreleased).
601 lines
16 KiB
C++
601 lines
16 KiB
C++
#include "mysql_stmt.h"
|
|
#include "eqemu_logsys.h"
|
|
#include "mutex.h"
|
|
#include "timer.h"
|
|
#include <charconv>
|
|
|
|
namespace mysql
|
|
{
|
|
|
|
void PreparedStmt::StmtDeleter::operator()(MYSQL_STMT* stmt) noexcept
|
|
{
|
|
// The connection must be locked when closing the stmt to avoid mysql errors
|
|
// in case another thread tries to use it during the close. If the mutex is
|
|
// changed to one that throws then exceptions need to be caught here.
|
|
LockMutex lock(mutex);
|
|
mysql_stmt_close(stmt);
|
|
}
|
|
|
|
PreparedStmt::PreparedStmt(MYSQL& mysql, std::string query, Mutex* mutex, StmtOptions opts)
|
|
: m_stmt(mysql_stmt_init(&mysql), { mutex }), m_query(std::move(query)), m_mutex(mutex), m_options(opts)
|
|
{
|
|
LockMutex lock(m_mutex);
|
|
if (mysql_stmt_prepare(m_stmt.get(), m_query.c_str(), static_cast<unsigned long>(m_query.size())) != 0)
|
|
{
|
|
ThrowError(fmt::format("Prepare error: {}", GetStmtError()));
|
|
}
|
|
|
|
m_params.resize(mysql_stmt_param_count(m_stmt.get()));
|
|
m_inputs.resize(m_params.size());
|
|
}
|
|
|
|
void PreparedStmt::ThrowError(const std::string& error)
|
|
{
|
|
LogMySQLError("{}", error);
|
|
throw std::runtime_error(error);
|
|
}
|
|
|
|
std::string PreparedStmt::GetStmtError()
|
|
{
|
|
auto err = mysql_stmt_errno(m_stmt.get());
|
|
auto str = mysql_stmt_error(m_stmt.get());
|
|
return fmt::format("({}) [{}] for query [{}]", err, str, m_query);
|
|
}
|
|
|
|
template <typename T>
|
|
void PreparedStmt::BindInput(size_t index, T value)
|
|
{
|
|
if (index >= m_inputs.size())
|
|
{
|
|
ThrowError(fmt::format("Cannot bind input, index {} out of range", index));
|
|
}
|
|
|
|
impl::Bind& arg = m_inputs[index];
|
|
arg.is_null = std::is_same_v<T, std::nullptr_t>;
|
|
|
|
MYSQL_BIND& bind = m_params[index];
|
|
bind.is_unsigned = std::is_unsigned_v<T>;
|
|
bind.is_null = &arg.is_null;
|
|
bind.length = &arg.length;
|
|
|
|
auto old_type = bind.buffer_type;
|
|
|
|
if constexpr (std::is_arithmetic_v<T>)
|
|
{
|
|
if (arg.buffer.size() < sizeof(T))
|
|
{
|
|
arg.buffer.resize(std::max(sizeof(T), sizeof(int64_t)));
|
|
bind.buffer = arg.buffer.data();
|
|
m_need_bind = true;
|
|
}
|
|
memcpy(arg.buffer.data(), &value, sizeof(T));
|
|
}
|
|
|
|
if constexpr (std::is_same_v<T, int8_t> || std::is_same_v<T, uint8_t> || std::is_same_v<T, bool>)
|
|
{
|
|
bind.buffer_type = MYSQL_TYPE_TINY;
|
|
}
|
|
else if constexpr (std::is_same_v<T, int16_t> || std::is_same_v<T, uint16_t>)
|
|
{
|
|
bind.buffer_type = MYSQL_TYPE_SHORT;
|
|
}
|
|
else if constexpr (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>)
|
|
{
|
|
bind.buffer_type = MYSQL_TYPE_LONG;
|
|
}
|
|
else if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>)
|
|
{
|
|
bind.buffer_type = MYSQL_TYPE_LONGLONG;
|
|
}
|
|
else if constexpr (std::is_same_v<T, float>)
|
|
{
|
|
bind.buffer_type = MYSQL_TYPE_FLOAT;
|
|
}
|
|
else if constexpr (std::is_same_v<T, double>)
|
|
{
|
|
bind.buffer_type = MYSQL_TYPE_DOUBLE;
|
|
}
|
|
else if constexpr (std::is_same_v<T, std::string_view>)
|
|
{
|
|
bind.buffer_type = MYSQL_TYPE_STRING;
|
|
if (arg.buffer.empty() || arg.buffer.size() < value.size())
|
|
{
|
|
arg.buffer.resize(static_cast<size_t>((value.size() + 1) * 1.5));
|
|
bind.buffer = arg.buffer.data();
|
|
bind.buffer_length = static_cast<unsigned long>(arg.buffer.size());
|
|
m_need_bind = true;
|
|
}
|
|
std::copy(value.begin(), value.end(), arg.buffer.begin());
|
|
arg.length = static_cast<unsigned long>(value.size());
|
|
}
|
|
else if constexpr (!std::is_same_v<T, std::nullptr_t>)
|
|
{
|
|
static_assert(false_v<T>, "Cannot bind unsupported type");
|
|
}
|
|
|
|
if (old_type != bind.buffer_type)
|
|
{
|
|
m_need_bind = true;
|
|
}
|
|
}
|
|
|
|
void PreparedStmt::BindInput(size_t index, const char* str)
|
|
{
|
|
BindInput(index, std::string_view(str));
|
|
}
|
|
|
|
void PreparedStmt::BindInput(size_t index, const std::string& str)
|
|
{
|
|
BindInput(index, std::string_view(str));
|
|
}
|
|
|
|
StmtResult PreparedStmt::Execute()
|
|
{
|
|
CheckArgs(0);
|
|
return DoExecute();
|
|
}
|
|
|
|
StmtResult PreparedStmt::Execute(const std::vector<param_t>& args)
|
|
{
|
|
CheckArgs(args.size());
|
|
for (size_t i = 0; i < args.size(); ++i)
|
|
{
|
|
std::visit([&](const auto& arg) { BindInput(i, arg); }, args[i]);
|
|
}
|
|
return DoExecute();
|
|
}
|
|
|
|
template <typename T>
|
|
StmtResult PreparedStmt::Execute(const std::vector<T>& args)
|
|
{
|
|
CheckArgs(args.size());
|
|
for (size_t i = 0; i < args.size(); ++i)
|
|
{
|
|
BindInput(i, args[i]);
|
|
}
|
|
return DoExecute();
|
|
}
|
|
|
|
void PreparedStmt::CheckArgs(size_t argc)
|
|
{
|
|
if (argc != m_params.size())
|
|
{
|
|
ThrowError(fmt::format("Bad arg count (got {}, expected {}) for [{}]", argc, m_params.size(), m_query));
|
|
}
|
|
}
|
|
|
|
StmtResult PreparedStmt::DoExecute()
|
|
{
|
|
BenchTimer timer;
|
|
LockMutex lock(m_mutex);
|
|
|
|
if (m_need_bind && mysql_stmt_bind_param(m_stmt.get(), m_params.data()) != 0)
|
|
{
|
|
ThrowError(fmt::format("Bind param error: {}", GetStmtError()));
|
|
}
|
|
|
|
m_need_bind = false;
|
|
|
|
if (mysql_stmt_execute(m_stmt.get()) != 0)
|
|
{
|
|
ThrowError(fmt::format("Execute error: {}", GetStmtError()));
|
|
}
|
|
|
|
my_bool attr = m_options.use_max_length;
|
|
mysql_stmt_attr_set(m_stmt.get(), STMT_ATTR_UPDATE_MAX_LENGTH, &attr);
|
|
|
|
if (m_options.buffer_results && mysql_stmt_store_result(m_stmt.get()) != 0)
|
|
{
|
|
ThrowError(fmt::format("Store result error: {}", GetStmtError()));
|
|
}
|
|
|
|
// Result buffers are bound on first execute and re-used if needed
|
|
if (m_results.empty())
|
|
{
|
|
BindResults();
|
|
}
|
|
|
|
StmtResult res(m_stmt.get(), m_results.size());
|
|
|
|
if (m_results.empty())
|
|
{
|
|
LogMySQLQuery("{} -- ({} row(s) affected) ({:.6f}s)", m_query, res.RowsAffected(), timer.elapsed());
|
|
}
|
|
else
|
|
{
|
|
LogMySQLQuery("{} -- ({} row(s) returned) ({:.6f}s)", m_query, res.RowCount(), timer.elapsed());
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void PreparedStmt::BindResults()
|
|
{
|
|
MYSQL_RES* res = mysql_stmt_result_metadata(m_stmt.get());
|
|
if (!res)
|
|
{
|
|
return; // did not produce a result set
|
|
}
|
|
|
|
MYSQL_FIELD* fields = mysql_fetch_fields(res);
|
|
m_columns.resize(mysql_num_fields(res));
|
|
m_results.resize(m_columns.size());
|
|
|
|
for (int i = 0; i < static_cast<int>(m_columns.size()); ++i)
|
|
{
|
|
impl::BindColumn& col = m_columns[i].m_col;
|
|
MYSQL_BIND& bind = m_results[i];
|
|
|
|
col.index = i;
|
|
col.name = fields[i].name;
|
|
col.buffer_type = fields[i].type;
|
|
col.is_unsigned = (fields[i].flags & UNSIGNED_FLAG) != 0;
|
|
col.buffer.resize(GetResultBufferSize(fields[i]));
|
|
|
|
bind.buffer_type = col.buffer_type;
|
|
bind.buffer = col.buffer.data();
|
|
bind.buffer_length = static_cast<unsigned long>(col.buffer.size());
|
|
bind.is_unsigned = col.is_unsigned;
|
|
bind.is_null = &col.is_null;
|
|
bind.length = &col.length;
|
|
bind.error = &col.error;
|
|
}
|
|
|
|
mysql_free_result(res);
|
|
|
|
if (!m_results.empty() && mysql_stmt_bind_result(m_stmt.get(), m_results.data()) != 0)
|
|
{
|
|
ThrowError(fmt::format("Bind result error: {}", GetStmtError()));
|
|
}
|
|
}
|
|
|
|
int PreparedStmt::GetResultBufferSize(const MYSQL_FIELD& field) const
|
|
{
|
|
switch (field.type)
|
|
{
|
|
case MYSQL_TYPE_TINY:
|
|
return sizeof(int8_t);
|
|
case MYSQL_TYPE_SHORT:
|
|
return sizeof(int16_t);
|
|
case MYSQL_TYPE_INT24:
|
|
case MYSQL_TYPE_LONG:
|
|
return sizeof(int32_t);
|
|
case MYSQL_TYPE_LONGLONG:
|
|
return sizeof(int64_t);
|
|
case MYSQL_TYPE_FLOAT:
|
|
return sizeof(float);
|
|
case MYSQL_TYPE_DOUBLE:
|
|
return sizeof(double);
|
|
case MYSQL_TYPE_TIME:
|
|
case MYSQL_TYPE_DATE:
|
|
case MYSQL_TYPE_DATETIME:
|
|
case MYSQL_TYPE_TIMESTAMP:
|
|
return sizeof(MYSQL_TIME);
|
|
default: // if max_length is unavailable for strings buffers are resized on fetch
|
|
return field.max_length + 1; // ensure valid buffer created
|
|
}
|
|
}
|
|
|
|
StmtRow PreparedStmt::Fetch()
|
|
{
|
|
StmtRow row;
|
|
if (!m_columns.empty())
|
|
{
|
|
int rc = mysql_stmt_fetch(m_stmt.get());
|
|
if (rc == 1)
|
|
{
|
|
ThrowError(fmt::format("Fetch error: {}", GetStmtError()));
|
|
}
|
|
|
|
if (rc != MYSQL_NO_DATA)
|
|
{
|
|
if (rc == MYSQL_DATA_TRUNCATED)
|
|
{
|
|
FetchTruncated();
|
|
}
|
|
row = StmtRow(m_columns);
|
|
}
|
|
}
|
|
return row;
|
|
}
|
|
|
|
void PreparedStmt::FetchTruncated()
|
|
{
|
|
for (int i = 0; i < static_cast<int>(m_columns.size()); ++i)
|
|
{
|
|
impl::BindColumn& col = m_columns[i].m_col;
|
|
if (col.error)
|
|
{
|
|
MYSQL_BIND& bind = m_results[i];
|
|
col.buffer.resize(static_cast<size_t>(col.length * 1.5));
|
|
bind.buffer = col.buffer.data();
|
|
bind.buffer_length = static_cast<unsigned long>(col.buffer.size());
|
|
|
|
mysql_stmt_fetch_column(m_stmt.get(), &bind, i, 0);
|
|
}
|
|
}
|
|
|
|
if (mysql_stmt_bind_result(m_stmt.get(), m_results.data()) != 0)
|
|
{
|
|
ThrowError(fmt::format("Fetch rebind result error: {}", GetStmtError()));
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
StmtResult::StmtResult(MYSQL_STMT* stmt, size_t columns)
|
|
{
|
|
m_num_cols = static_cast<int>(columns);
|
|
m_num_rows = mysql_stmt_num_rows(stmt); // requires buffered results
|
|
m_affected = mysql_stmt_affected_rows(stmt);
|
|
m_insert_id = mysql_stmt_insert_id(stmt);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const StmtColumn* StmtRow::GetColumn(size_t index) const
|
|
{
|
|
return index < m_columns.size() ? &m_columns[index] : nullptr;
|
|
}
|
|
|
|
const StmtColumn* StmtRow::GetColumn(std::string_view name) const
|
|
{
|
|
auto it = std::ranges::find_if(m_columns,
|
|
[name](const StmtColumn& col) { return col.Name() == name; });
|
|
|
|
return it != m_columns.end() ? &(*it) : nullptr;
|
|
}
|
|
|
|
std::optional<std::string> StmtRow::operator[](size_t index) const
|
|
{
|
|
return GetStr(index);
|
|
}
|
|
|
|
std::optional<std::string> StmtRow::operator[](std::string_view name) const
|
|
{
|
|
return GetStr(name);
|
|
}
|
|
|
|
std::optional<std::string> StmtRow::GetStr(size_t index) const
|
|
{
|
|
const StmtColumn* col = GetColumn(index);
|
|
return col ? col->GetStr() : std::nullopt;
|
|
}
|
|
|
|
std::optional<std::string> StmtRow::GetStr(std::string_view name) const
|
|
{
|
|
const StmtColumn* col = GetColumn(name);
|
|
return col ? col->GetStr() : std::nullopt;
|
|
}
|
|
|
|
template <typename T> requires std::is_arithmetic_v<T>
|
|
std::optional<T> StmtRow::Get(size_t index) const
|
|
{
|
|
const StmtColumn* col = GetColumn(index);
|
|
return col ? col->Get<T>() : std::nullopt;
|
|
}
|
|
|
|
template <typename T> requires std::is_arithmetic_v<T>
|
|
std::optional<T> StmtRow::Get(std::string_view name) const
|
|
{
|
|
const StmtColumn* col = GetColumn(name);
|
|
return col ? col->Get<T>() : std::nullopt;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static time_t MakeTime(const MYSQL_TIME& mt)
|
|
{
|
|
// buffer mt given in mysql session time zone (assumes local)
|
|
std::tm tm{};
|
|
tm.tm_year = mt.year - 1900;
|
|
tm.tm_mon = mt.month - 1;
|
|
tm.tm_mday = mt.day;
|
|
tm.tm_hour = mt.hour;
|
|
tm.tm_min = mt.minute;
|
|
tm.tm_sec = mt.second;
|
|
tm.tm_isdst = -1;
|
|
return std::mktime(&tm);
|
|
}
|
|
|
|
static int MakeSeconds(const MYSQL_TIME& mt)
|
|
{
|
|
return (mt.neg ? -1 : 1) * static_cast<int>(mt.hour * 3600 + mt.minute * 60 + mt.second);
|
|
}
|
|
|
|
static uint64_t MakeBits(std::span<const uint8_t> data)
|
|
{
|
|
// byte stream for bits is in big endian
|
|
uint64_t bits = 0;
|
|
for (size_t i = 0; i < data.size() && i < sizeof(uint64_t); ++i)
|
|
{
|
|
bits |= static_cast<uint64_t>(data[data.size() - i - 1] & 0xff) << (i * 8);
|
|
}
|
|
return bits;
|
|
}
|
|
|
|
template <typename T>
|
|
concept has_from_chars = requires (const char* first, const char* last, T value)
|
|
{
|
|
std::from_chars(first, last, value);
|
|
};
|
|
|
|
template <typename T>
|
|
static T FromString(std::string_view sv)
|
|
{
|
|
if constexpr (std::is_same_v<T, bool>)
|
|
{
|
|
// return false for empty (zero-length) strings
|
|
return !sv.empty();
|
|
}
|
|
else if constexpr (std::is_same_v<T, float> && !has_from_chars<T>)
|
|
{
|
|
return std::strtof(std::string(sv).c_str(), nullptr);
|
|
}
|
|
else if constexpr (std::is_same_v<T, double> && !has_from_chars<T>)
|
|
{
|
|
return std::strtod(std::string(sv).c_str(), nullptr);
|
|
}
|
|
else
|
|
{
|
|
// non numbers return a zero initialized T (could return nullopt instead)
|
|
T value = {};
|
|
std::from_chars(sv.data(), sv.data() + sv.size(), value);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
static std::string FormatTime(enum_field_types type, const MYSQL_TIME& mt)
|
|
{
|
|
switch (type)
|
|
{
|
|
case MYSQL_TYPE_TIME: // hhh:mm:ss '-838:59:59' to '838:59:59'
|
|
return fmt::format("{}{:02d}:{:02d}:{:02d}", mt.neg ? "-" : "", mt.hour, mt.minute, mt.second);
|
|
case MYSQL_TYPE_DATE: // YYYY-MM-DD '1000-01-01' to '9999-12-31'
|
|
return fmt::format("{}-{:02d}-{:02d}", mt.year, mt.month, mt.day);
|
|
case MYSQL_TYPE_DATETIME: // YYYY-MM-DD hh:mm:ss '1000-01-01 00:00:00' to '9999-12-31 23:59:59'
|
|
case MYSQL_TYPE_TIMESTAMP: // YYYY-MM-DD hh:mm:ss '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC
|
|
return fmt::format("{}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}", mt.year, mt.month, mt.day, mt.hour, mt.minute, mt.second);
|
|
default:
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
std::optional<std::string_view> StmtColumn::GetStrView() const
|
|
{
|
|
if (m_col.is_null)
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
|
|
switch (m_col.buffer_type)
|
|
{
|
|
case MYSQL_TYPE_NEWDECIMAL:
|
|
case MYSQL_TYPE_TINY_BLOB:
|
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
case MYSQL_TYPE_LONG_BLOB:
|
|
case MYSQL_TYPE_BLOB:
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING:
|
|
return std::make_optional<std::string_view>(reinterpret_cast<const char*>(m_col.buffer.data()), m_col.length);
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<std::string> StmtColumn::GetStr() const
|
|
{
|
|
if (m_col.is_null)
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
|
|
switch (m_col.buffer_type)
|
|
{
|
|
case MYSQL_TYPE_TINY:
|
|
return m_col.is_unsigned ? fmt::format_int(BitCast<uint8_t>()).c_str() : fmt::format_int(BitCast<int8_t>()).c_str();
|
|
case MYSQL_TYPE_SHORT:
|
|
return m_col.is_unsigned ? fmt::format_int(BitCast<uint16_t>()).c_str() : fmt::format_int(BitCast<int16_t>()).c_str();
|
|
case MYSQL_TYPE_INT24:
|
|
case MYSQL_TYPE_LONG:
|
|
return m_col.is_unsigned ? fmt::format_int(BitCast<uint32_t>()).c_str() : fmt::format_int(BitCast<int32_t>()).c_str();
|
|
case MYSQL_TYPE_LONGLONG:
|
|
return m_col.is_unsigned ? fmt::format_int(BitCast<uint64_t>()).c_str() : fmt::format_int(BitCast<int64_t>()).c_str();
|
|
case MYSQL_TYPE_FLOAT:
|
|
return fmt::format("{}", BitCast<float>());
|
|
case MYSQL_TYPE_DOUBLE:
|
|
return fmt::format("{}", BitCast<double>());
|
|
case MYSQL_TYPE_TIME:
|
|
case MYSQL_TYPE_DATE:
|
|
case MYSQL_TYPE_DATETIME:
|
|
case MYSQL_TYPE_TIMESTAMP:
|
|
return FormatTime(m_col.buffer_type, BitCast<MYSQL_TIME>());
|
|
case MYSQL_TYPE_BIT:
|
|
return fmt::format_int(*Get<uint64_t>()).c_str();
|
|
case MYSQL_TYPE_NEWDECIMAL:
|
|
case MYSQL_TYPE_TINY_BLOB:
|
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
case MYSQL_TYPE_LONG_BLOB:
|
|
case MYSQL_TYPE_BLOB:
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING:
|
|
return std::make_optional<std::string>(reinterpret_cast<const char*>(m_col.buffer.data()), m_col.length);
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
template <typename T> requires std::is_arithmetic_v<T>
|
|
std::optional<T> StmtColumn::Get() const
|
|
{
|
|
if (m_col.is_null)
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
|
|
switch (m_col.buffer_type)
|
|
{
|
|
case MYSQL_TYPE_TINY:
|
|
return m_col.is_unsigned ? static_cast<T>(BitCast<uint8_t>()) : static_cast<T>(BitCast<int8_t>());
|
|
case MYSQL_TYPE_SHORT:
|
|
return m_col.is_unsigned ? static_cast<T>(BitCast<uint16_t>()) : static_cast<T>(BitCast<int16_t>());
|
|
case MYSQL_TYPE_INT24:
|
|
case MYSQL_TYPE_LONG:
|
|
return m_col.is_unsigned ? static_cast<T>(BitCast<uint32_t>()) : static_cast<T>(BitCast<int32_t>());
|
|
case MYSQL_TYPE_LONGLONG:
|
|
return m_col.is_unsigned ? static_cast<T>(BitCast<uint64_t>()) : static_cast<T>(BitCast<int64_t>());
|
|
case MYSQL_TYPE_FLOAT:
|
|
return static_cast<T>(BitCast<float>());
|
|
case MYSQL_TYPE_DOUBLE:
|
|
return static_cast<T>(BitCast<double>());
|
|
case MYSQL_TYPE_TIME: // return as total seconds
|
|
return static_cast<T>(MakeSeconds(BitCast<MYSQL_TIME>()));
|
|
case MYSQL_TYPE_DATE:
|
|
case MYSQL_TYPE_DATETIME:
|
|
case MYSQL_TYPE_TIMESTAMP: // return as epoch timestamp
|
|
return static_cast<T>(MakeTime(BitCast<MYSQL_TIME>()));
|
|
case MYSQL_TYPE_BIT:
|
|
return static_cast<T>(MakeBits({ m_col.buffer.data(), m_col.length }));
|
|
case MYSQL_TYPE_NEWDECIMAL:
|
|
case MYSQL_TYPE_TINY_BLOB:
|
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
|
case MYSQL_TYPE_LONG_BLOB:
|
|
case MYSQL_TYPE_BLOB:
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING:
|
|
return FromString<T>({ reinterpret_cast<const char*>(m_col.buffer.data()), m_col.length });
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// explicit template instantiations for supported types
|
|
template void PreparedStmt::BindInput(size_t, std::string_view);
|
|
template void PreparedStmt::BindInput(size_t, std::nullptr_t);
|
|
template StmtResult PreparedStmt::Execute(const std::vector<std::string_view>&);
|
|
template StmtResult PreparedStmt::Execute(const std::vector<std::string>&);
|
|
template StmtResult PreparedStmt::Execute(const std::vector<const char*>&);
|
|
|
|
#define INSTANTIATE(T) \
|
|
template void PreparedStmt::BindInput(size_t, T); \
|
|
template StmtResult PreparedStmt::Execute(const std::vector<T>&); \
|
|
template std::optional<T> StmtRow::Get(size_t) const; \
|
|
template std::optional<T> StmtRow::Get(std::string_view) const; \
|
|
template std::optional<T> StmtColumn::Get() const;
|
|
|
|
INSTANTIATE(bool);
|
|
INSTANTIATE(int8_t);
|
|
INSTANTIATE(uint8_t);
|
|
INSTANTIATE(int16_t);
|
|
INSTANTIATE(uint16_t);
|
|
INSTANTIATE(int32_t);
|
|
INSTANTIATE(uint32_t);
|
|
INSTANTIATE(int64_t);
|
|
INSTANTIATE(uint64_t);
|
|
INSTANTIATE(float);
|
|
INSTANTIATE(double);
|
|
|
|
} // namespace mysql
|