Store error status into QueryResult object
The QueryResult-object remembers if a conversion failed. This makes checking for errors more convenient, as just one check per row is required. The conversion functions always return a valid value.
This commit is contained in:
@ -16,6 +16,7 @@
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <maxbase/assert.h>
|
||||
#include <maxbase/format.hh>
|
||||
|
||||
using std::string;
|
||||
|
||||
@ -116,6 +117,7 @@ bool QueryResult::next_row()
|
||||
if (m_rowdata)
|
||||
{
|
||||
m_current_row_ind++;
|
||||
m_error = ConversionError(); // Reset error
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -153,29 +155,147 @@ string QueryResult::get_string(int64_t column_ind) const
|
||||
return data ? data : "";
|
||||
}
|
||||
|
||||
int64_t QueryResult::get_uint(int64_t column_ind) const
|
||||
int64_t QueryResult::get_int(int64_t column_ind) const
|
||||
{
|
||||
return parse_integer(column_ind, "integer");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a 64bit integer. On parse error an error flag is set.
|
||||
*
|
||||
* @param column_ind Column index
|
||||
* @param target_type The final conversion target type.
|
||||
* @return Conversion result
|
||||
*/
|
||||
int64_t QueryResult::parse_integer(int64_t column_ind, const std::string& target_type) const
|
||||
{
|
||||
mxb_assert(column_ind < get_col_count() && column_ind >= 0 && m_rowdata);
|
||||
char* data = m_rowdata[column_ind];
|
||||
int64_t rval = -1;
|
||||
if (data && *data)
|
||||
int64_t rval = 0;
|
||||
char* data_elem = m_rowdata[column_ind];
|
||||
if (data_elem == nullptr)
|
||||
{
|
||||
errno = 0; // strtoll sets this
|
||||
char* endptr = NULL;
|
||||
auto parsed = strtoll(data, &endptr, 10);
|
||||
if (parsed >= 0 && errno == 0 && *endptr == '\0')
|
||||
set_error(column_ind, target_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = 0;
|
||||
char* endptr = nullptr;
|
||||
auto parsed = strtoll(data_elem, &endptr, 10);
|
||||
if (errno == 0 && *endptr == '\0')
|
||||
{
|
||||
rval = parsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_error(column_ind, target_type);
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool QueryResult::get_bool(int64_t column_ind) const
|
||||
{
|
||||
const string target_type = "boolean";
|
||||
bool rval = false;
|
||||
auto val = parse_integer(column_ind, target_type);
|
||||
if (!error())
|
||||
{
|
||||
if (val < 0 || val > 1)
|
||||
{
|
||||
set_error(column_ind, target_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
rval = (val == 1);
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool QueryResult::field_is_null(int64_t column_ind) const
|
||||
{
|
||||
mxb_assert(column_ind < get_col_count() && column_ind >= 0 && m_rowdata);
|
||||
char* data = m_rowdata[column_ind];
|
||||
return data ? (strcmp(data, "Y") == 0 || strcmp(data, "1") == 0) : false;
|
||||
return m_rowdata[column_ind] == nullptr;
|
||||
}
|
||||
|
||||
void QueryResult::set_error(int64_t column_ind, const string& target_type) const
|
||||
{
|
||||
string col_name;
|
||||
// Find the column name.
|
||||
for (const auto& elem : m_col_indexes)
|
||||
{
|
||||
if (elem.second == column_ind)
|
||||
{
|
||||
col_name = elem.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mxb_assert(!col_name.empty());
|
||||
// If the field value is null, then that is the cause of the error.
|
||||
char* field_value = m_rowdata[column_ind];
|
||||
if (field_value == nullptr)
|
||||
{
|
||||
m_error.set_null_value_error(target_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_error.set_value_error(field_value, target_type);
|
||||
}
|
||||
}
|
||||
|
||||
bool QueryResult::error() const
|
||||
{
|
||||
return m_error.error();
|
||||
}
|
||||
|
||||
string QueryResult::error_string() const
|
||||
{
|
||||
return m_error.to_string();
|
||||
}
|
||||
|
||||
void QueryResult::ConversionError::set_value_error(const string& field_value, const string& target_type)
|
||||
{
|
||||
mxb_assert(!target_type.empty());
|
||||
// The error container only has space for one error.
|
||||
if (m_target_type.empty())
|
||||
{
|
||||
m_field_value = field_value;
|
||||
m_target_type = target_type;
|
||||
}
|
||||
}
|
||||
|
||||
void QueryResult::ConversionError::set_null_value_error(const string& target_type)
|
||||
{
|
||||
mxb_assert(!target_type.empty());
|
||||
if (m_target_type.empty())
|
||||
{
|
||||
m_field_was_null = true;
|
||||
m_target_type = target_type;
|
||||
}
|
||||
}
|
||||
|
||||
string QueryResult::ConversionError::to_string() const
|
||||
{
|
||||
string rval;
|
||||
if (!m_target_type.empty())
|
||||
{
|
||||
rval = "Cannot convert ";
|
||||
if (m_field_was_null)
|
||||
{
|
||||
rval += mxb::string_printf("a null field to %s.", m_target_type.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
rval += mxb::string_printf("field '%s' to %s.", m_field_value.c_str(), m_target_type.c_str());
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool QueryResult::ConversionError::error() const
|
||||
{
|
||||
return !m_target_type.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user