pica/database/mysql/statement.cpp

116 lines
3.9 KiB
C++
Raw Normal View History

// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
2023-12-08 22:26:16 +00:00
#include "statement.h"
#include <cstring>
2023-12-22 23:25:20 +00:00
#include "mysqld_error.h"
#include "database/exceptions.h"
2023-12-08 22:26:16 +00:00
static uint64_t TIME_LENGTH = sizeof(MYSQL_TIME);
MySQL::Statement::Statement(MYSQL* connection, const char* statement):
stmt(mysql_stmt_init(connection)),
2023-12-20 22:42:13 +00:00
param()
2023-12-08 22:26:16 +00:00
{
int result = mysql_stmt_prepare(stmt.get(), statement, strlen(statement));
if (result != 0)
throw std::runtime_error(std::string("Error preparing statement: ") + mysql_stmt_error(stmt.get()));
}
2023-12-20 22:42:13 +00:00
void MySQL::Statement::bind(void* value, enum_field_types type, bool usigned) {
2023-12-08 22:26:16 +00:00
MYSQL_BIND& result = param.emplace_back();
std::memset(&result, 0, sizeof(result));
result.buffer_type = type;
result.buffer = value;
switch (type) {
case MYSQL_TYPE_STRING:
case MYSQL_TYPE_VAR_STRING:
2023-12-20 22:42:13 +00:00
result.buffer_length = strlen(static_cast<char*>(value));
2023-12-08 22:26:16 +00:00
break;
case MYSQL_TYPE_DATE:
2023-12-20 22:42:13 +00:00
result.buffer_length = TIME_LENGTH;
break;
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_TINY:
result.is_unsigned = usigned;
2023-12-08 22:26:16 +00:00
break;
default:
throw std::runtime_error("Type: " + std::to_string(type) + " is not yet supported in bind");
break;
}
}
void MySQL::Statement::execute() {
2023-12-22 23:25:20 +00:00
MYSQL_STMT* raw = stmt.get();
int result = mysql_stmt_bind_param(raw, param.data());
2023-12-08 22:26:16 +00:00
if (result != 0)
2023-12-22 23:25:20 +00:00
throw std::runtime_error(std::string("Error binding statement: ") + mysql_stmt_error(raw));
2023-12-08 22:26:16 +00:00
2023-12-22 23:25:20 +00:00
result = mysql_stmt_execute(raw);
if (result != 0) {
int errcode = mysql_stmt_errno(raw);
std::string text = mysql_stmt_error(raw);
switch (errcode) {
case ER_DUP_ENTRY:
throw Duplicate("Error executing statement: " + text);
default:
throw std::runtime_error("Error executing statement: " + text);
}
}
}
std::vector<std::vector<std::string>> MySQL::Statement::fetchResult() {
MYSQL_STMT* raw = stmt.get();
if (mysql_stmt_store_result(raw) != 0)
throw std::runtime_error(std::string("Error fetching statement result: ") + mysql_stmt_error(raw)); //TODO not sure if it's valid here
MYSQL_RES* meta = mysql_stmt_result_metadata(raw);
if (meta == nullptr)
throw std::runtime_error(std::string("Error fetching statement result: ") + mysql_stmt_error(raw)); //TODO not sure if it's valid here
std::unique_ptr<MYSQL_RES, ResDeleter> mt(meta);
unsigned int numColumns = mysql_num_fields(meta);
MYSQL_BIND bind[numColumns];
memset(bind, 0, sizeof(bind));
std::vector<std::string> line(numColumns);
std::vector<long unsigned int> lengths(numColumns);
for (unsigned int i = 0; i < numColumns; ++i) {
MYSQL_FIELD *field = mysql_fetch_field_direct(meta, i);
switch (field->type) {
case MYSQL_TYPE_STRING:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_VARCHAR:
break;
default:
throw std::runtime_error("Unsupported data fetching statement result " + std::to_string(field->type));
}
line[i].resize(field->length);
bind[i].buffer_type = field->type;
bind[i].buffer = line[i].data();
bind[i].buffer_length = field->length;
bind[i].length = &lengths[i];
}
if (mysql_stmt_bind_result(raw, bind) != 0)
throw std::runtime_error(std::string("Error binding on fetching statement result: ") + mysql_stmt_error(raw));
std::vector<std::vector<std::string>> result;
while (mysql_stmt_fetch(raw) == 0) {
std::vector<std::string>& row = result.emplace_back(numColumns);
for (unsigned int i = 0; i < numColumns; ++i)
row[i] = std::string(line[i].data(), lengths[i]);
}
return result;
2023-12-08 22:26:16 +00:00
}
2023-12-22 23:25:20 +00:00