mirror of
https://github.com/rbock/sqlpp11.git
synced 2024-11-15 20:31:16 +08:00
Cleanup the code of the MySQL connector (#511)
* Move the method definitions for sqlpp::postgresql::detail::statement_handle_t inside the class. * Move the method definitions for sqlpp::postgresql::detail::prepared_statement_handle_t inside the class. * Move the method definitions for sqlpp::postgresql::prepared_statement_t inside the class. * Move the method definitions for sqlpp::postgresql::::Result inside the class. * Remove superfluous inline specifier from the in-class method definitions of sqlpp::postgresql::Result. * Remove unused method declaration sqlpp::postgresql::Result::hasError() * Move the method definitions for sqlpp::postgresql::bind_result_t inside the class. * Move the method definitions for sqlpp::postgresql::connection_base inside the class. * Add a comment explaining why sqlpp::postgresql::context_t::escape() is defined out-of-class. * Remove incorrect comment about forward declaration * Remove a superfluous function that forwards the call to PQFinish(). * Replace "std::string" parameters with "const std::string&". * Remove superfluous detail:: namespace qualification. * Rename class/struct types, variables and functions from CamelCase to snake_case.
This commit is contained in:
parent
c03ac660ee
commit
d800f4d6fa
@ -56,11 +56,55 @@ namespace sqlpp
|
|||||||
private:
|
private:
|
||||||
std::shared_ptr<detail::statement_handle_t> _handle;
|
std::shared_ptr<detail::statement_handle_t> _handle;
|
||||||
|
|
||||||
bool next_impl();
|
bool next_impl()
|
||||||
|
{
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: accessing next row of handle at " << _handle.get() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch total amount
|
||||||
|
if (_handle->total_count == 0U)
|
||||||
|
{
|
||||||
|
_handle->total_count = _handle->result.records_size();
|
||||||
|
if (_handle->total_count == 0U)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Next row
|
||||||
|
if (_handle->count < (_handle->total_count - 1))
|
||||||
|
{
|
||||||
|
_handle->count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Really needed?
|
||||||
|
if (_handle->fields == 0U)
|
||||||
|
{
|
||||||
|
_handle->fields = _handle->result.field_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bind_result_t() = default;
|
bind_result_t() = default;
|
||||||
bind_result_t(const std::shared_ptr<detail::statement_handle_t>& handle);
|
|
||||||
|
bind_result_t(const std::shared_ptr<detail::statement_handle_t>& handle) : _handle(handle)
|
||||||
|
{
|
||||||
|
if (this->_handle && this->_handle->debug())
|
||||||
|
{
|
||||||
|
// cerr
|
||||||
|
std::cerr << "PostgreSQL debug: constructing bind result, using handle at: " << this->_handle.get()
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bind_result_t(const bind_result_t&) = delete;
|
bind_result_t(const bind_result_t&) = delete;
|
||||||
bind_result_t(bind_result_t&&) = default;
|
bind_result_t(bind_result_t&&) = default;
|
||||||
bind_result_t& operator=(const bind_result_t&) = delete;
|
bind_result_t& operator=(const bind_result_t&) = delete;
|
||||||
@ -98,314 +142,254 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bind_boolean_result(size_t index, signed char* value, bool* is_null);
|
void _bind_boolean_result(size_t _index, signed char* value, bool* is_null)
|
||||||
void _bind_floating_point_result(size_t index, double* value, bool* is_null);
|
|
||||||
void _bind_integral_result(size_t index, int64_t* value, bool* is_null);
|
|
||||||
void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null);
|
|
||||||
void _bind_text_result(size_t index, const char** value, size_t* len);
|
|
||||||
void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null);
|
|
||||||
void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null);
|
|
||||||
void _bind_time_of_day_result(size_t index, ::std::chrono::microseconds* value, bool* is_null);
|
|
||||||
void _bind_blob_result(size_t index, const uint8_t** value, size_t* len);
|
|
||||||
|
|
||||||
int size() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bind_result_t::bind_result_t(const std::shared_ptr<detail::statement_handle_t>& handle) : _handle(handle)
|
|
||||||
{
|
|
||||||
if (this->_handle && this->_handle->debug())
|
|
||||||
{
|
{
|
||||||
// cerr
|
auto index = static_cast<int>(_index);
|
||||||
std::cerr << "PostgreSQL debug: constructing bind result, using handle at: " << this->_handle.get()
|
if (_handle->debug())
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool bind_result_t::next_impl()
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: accessing next row of handle at " << _handle.get() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch total amount
|
|
||||||
if (_handle->totalCount == 0U)
|
|
||||||
{
|
|
||||||
_handle->totalCount = _handle->result.records_size();
|
|
||||||
if (_handle->totalCount == 0U)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Next row
|
|
||||||
if (_handle->count < (_handle->totalCount - 1))
|
|
||||||
{
|
{
|
||||||
_handle->count++;
|
std::cerr << "PostgreSQL debug: binding boolean result at index: " << index << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
|
*value = _handle->result.get_bool_value(_handle->count, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_floating_point_result(size_t _index, double* value, bool* is_null)
|
||||||
|
{
|
||||||
|
auto index = static_cast<int>(_index);
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding floating_point result at index: " << index << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
|
*value = _handle->result.get_double_value(_handle->count, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_integral_result(size_t _index, int64_t* value, bool* is_null)
|
||||||
|
{
|
||||||
|
auto index = static_cast<int>(_index);
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding integral result at index: " << index << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
|
*value = _handle->result.get_int64_value(_handle->count, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_unsigned_integral_result(size_t _index, uint64_t* value, bool* is_null)
|
||||||
|
{
|
||||||
|
auto index = static_cast<int>(_index);
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding unsigned integral result at index: " << index << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
|
*value = _handle->result.get_uint64_value(_handle->count, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_text_result(size_t _index, const char** value, size_t* len)
|
||||||
|
{
|
||||||
|
auto index = static_cast<int>(_index);
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding text result at index: " << index << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_handle->result.is_null(_handle->count, index))
|
||||||
|
{
|
||||||
|
*value = nullptr;
|
||||||
|
*len = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
*value = _handle->result.get_char_ptr_value(_handle->count, index);
|
||||||
|
*len = static_cast<size_t>(_handle->result.length(_handle->count, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Really needed?
|
// PostgreSQL will return one of those (using the default ISO client):
|
||||||
if (_handle->fields == 0U)
|
//
|
||||||
|
// 2010-10-11 01:02:03 - ISO timestamp without timezone
|
||||||
|
// 2011-11-12 01:02:03.123456 - ISO timesapt with sub-second (microsecond) precision
|
||||||
|
// 1997-12-17 07:37:16-08 - ISO timestamp with timezone
|
||||||
|
// 1992-10-10 01:02:03-06:30 - for some timezones with non-hour offset
|
||||||
|
// 1900-01-01 - date only
|
||||||
|
// we do not support time-only values !
|
||||||
|
void _bind_date_result(size_t _index, ::sqlpp::chrono::day_point* value, bool* is_null)
|
||||||
{
|
{
|
||||||
_handle->fields = _handle->result.field_count();
|
auto index = static_cast<int>(_index);
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void bind_result_t::_bind_boolean_result(size_t _index, signed char* value, bool* is_null)
|
|
||||||
{
|
|
||||||
auto index = static_cast<int>(_index);
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding boolean result at index: " << index << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
*is_null = _handle->result.isNull(_handle->count, index);
|
|
||||||
*value = _handle->result.getBoolValue(_handle->count, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void bind_result_t::_bind_floating_point_result(size_t _index, double* value, bool* is_null)
|
|
||||||
{
|
|
||||||
auto index = static_cast<int>(_index);
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding floating_point result at index: " << index << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
*is_null = _handle->result.isNull(_handle->count, index);
|
|
||||||
*value = _handle->result.getDoubleValue(_handle->count, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void bind_result_t::_bind_integral_result(size_t _index, int64_t* value, bool* is_null)
|
|
||||||
{
|
|
||||||
auto index = static_cast<int>(_index);
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding integral result at index: " << index << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
*is_null = _handle->result.isNull(_handle->count, index);
|
|
||||||
*value = _handle->result.getInt64Value(_handle->count, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void bind_result_t::_bind_unsigned_integral_result(size_t _index, uint64_t* value, bool* is_null)
|
|
||||||
{
|
|
||||||
auto index = static_cast<int>(_index);
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding unsigned integral result at index: " << index << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
*is_null = _handle->result.isNull(_handle->count, index);
|
|
||||||
*value = _handle->result.getUInt64Value(_handle->count, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void bind_result_t::_bind_text_result(size_t _index, const char** value, size_t* len)
|
|
||||||
{
|
|
||||||
auto index = static_cast<int>(_index);
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding text result at index: " << index << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_handle->result.isNull(_handle->count, index))
|
|
||||||
{
|
|
||||||
*value = nullptr;
|
|
||||||
*len = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*value = _handle->result.getCharPtrValue(_handle->count, index);
|
|
||||||
*len = static_cast<size_t>(_handle->result.length(_handle->count, index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostgreSQL will return one of those (using the default ISO client):
|
|
||||||
//
|
|
||||||
// 2010-10-11 01:02:03 - ISO timestamp without timezone
|
|
||||||
// 2011-11-12 01:02:03.123456 - ISO timesapt with sub-second (microsecond) precision
|
|
||||||
// 1997-12-17 07:37:16-08 - ISO timestamp with timezone
|
|
||||||
// 1992-10-10 01:02:03-06:30 - for some timezones with non-hour offset
|
|
||||||
// 1900-01-01 - date only
|
|
||||||
// we do not support time-only values !
|
|
||||||
|
|
||||||
inline void bind_result_t::_bind_date_result(size_t _index, ::sqlpp::chrono::day_point* value, bool* is_null)
|
|
||||||
{
|
|
||||||
auto index = static_cast<int>(_index);
|
|
||||||
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding date result at index: " << index << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
*is_null = _handle->result.isNull(_handle->count, index);
|
|
||||||
|
|
||||||
if (!(*is_null))
|
|
||||||
{
|
|
||||||
const auto date_string = _handle->result.getCharPtrValue(_handle->count, index);
|
|
||||||
|
|
||||||
if (_handle->debug())
|
if (_handle->debug())
|
||||||
{
|
{
|
||||||
std::cerr << "PostgreSQL debug: date string: " << date_string << std::endl;
|
std::cerr << "PostgreSQL debug: binding date result at index: " << index << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::regex rx {"(\\d{4})-(\\d{2})-(\\d{2})"};
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
std::cmatch mr;
|
|
||||||
if (std::regex_match (date_string, mr, rx)) {
|
if (!(*is_null))
|
||||||
*value =
|
{
|
||||||
::sqlpp::chrono::day_point{
|
const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index);
|
||||||
::date::year{std::atoi(date_string + mr.position(1))} / // Year
|
|
||||||
std::atoi(date_string + mr.position(2)) / // Month
|
if (_handle->debug())
|
||||||
std::atoi(date_string + mr.position(3)) // Day of month
|
{
|
||||||
};
|
std::cerr << "PostgreSQL debug: date string: " << date_string << std::endl;
|
||||||
} else {
|
|
||||||
if (_handle->debug()) {
|
|
||||||
std::cerr << "PostgreSQL debug: got invalid date '" << date_string << "'" << std::endl;
|
|
||||||
}
|
}
|
||||||
*value = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*value = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// always returns UTC time for timestamp with time zone
|
static const std::regex rx {"(\\d{4})-(\\d{2})-(\\d{2})"};
|
||||||
inline void bind_result_t::_bind_date_time_result(size_t _index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
|
std::cmatch mr;
|
||||||
{
|
if (std::regex_match (date_string, mr, rx)) {
|
||||||
auto index = static_cast<int>(_index);
|
*value =
|
||||||
if (_handle->debug())
|
::sqlpp::chrono::day_point{
|
||||||
{
|
::date::year{std::atoi(date_string + mr.position(1))} / // Year
|
||||||
std::cerr << "PostgreSQL debug: binding date_time result at index: " << index << std::endl;
|
std::atoi(date_string + mr.position(2)) / // Month
|
||||||
}
|
std::atoi(date_string + mr.position(3)) // Day of month
|
||||||
|
};
|
||||||
*is_null = _handle->result.isNull(_handle->count, index);
|
} else {
|
||||||
|
if (_handle->debug()) {
|
||||||
if (!(*is_null))
|
std::cerr << "PostgreSQL debug: got invalid date '" << date_string << "'" << std::endl;
|
||||||
{
|
|
||||||
const auto date_string = _handle->result.getCharPtrValue(_handle->count, index);
|
|
||||||
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: got date_time string: " << date_string << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const std::regex rx {
|
|
||||||
"(\\d{4})-(\\d{2})-(\\d{2}) "
|
|
||||||
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
|
||||||
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
|
||||||
};
|
|
||||||
std::cmatch mr;
|
|
||||||
if (std::regex_match (date_string, mr, rx)) {
|
|
||||||
*value =
|
|
||||||
::sqlpp::chrono::day_point{
|
|
||||||
::date::year{std::atoi(date_string + mr.position(1))} / // Year
|
|
||||||
std::atoi(date_string + mr.position(2)) / // Month
|
|
||||||
std::atoi(date_string + mr.position(3)) // Day of month
|
|
||||||
} +
|
|
||||||
std::chrono::hours{std::atoi(date_string + mr.position(4))} + // Hour
|
|
||||||
std::chrono::minutes{std::atoi(date_string + mr.position(5))} + // Minute
|
|
||||||
std::chrono::seconds{std::atoi(date_string + mr.position(6))} + // Second
|
|
||||||
::std::chrono::microseconds{ // Microsecond
|
|
||||||
mr[7].matched ? std::stoi((mr[7].str() + "000000").substr(0, 6)) : 0
|
|
||||||
};
|
|
||||||
if (mr[8].matched) {
|
|
||||||
const auto tz_sign = (date_string[mr.position(8)] == '+') ? 1 : -1;
|
|
||||||
const auto tz_offset =
|
|
||||||
std::chrono::hours{std::atoi(date_string + mr.position(9))} +
|
|
||||||
std::chrono::minutes{mr[10].matched ? std::atoi(date_string + mr.position(10)) : 0} +
|
|
||||||
std::chrono::seconds{mr[11].matched ? std::atoi(date_string + mr.position(11)) : 0};
|
|
||||||
*value -= tz_sign * tz_offset;
|
|
||||||
}
|
}
|
||||||
} else {
|
*value = {};
|
||||||
if (_handle->debug()) {
|
|
||||||
std::cerr << "PostgreSQL debug: got invalid date_time '" << date_string << "'" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
*value = {};
|
*value = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// always returns UTC time for time with time zone
|
// always returns UTC time for timestamp with time zone
|
||||||
inline void bind_result_t::_bind_time_of_day_result(size_t _index, ::std::chrono::microseconds* value, bool* is_null)
|
void _bind_date_time_result(size_t _index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
|
||||||
{
|
|
||||||
auto index = static_cast<int>(_index);
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
{
|
||||||
std::cerr << "PostgreSQL debug: binding time result at index: " << index << std::endl;
|
auto index = static_cast<int>(_index);
|
||||||
}
|
|
||||||
|
|
||||||
*is_null = _handle->result.isNull(_handle->count, index);
|
|
||||||
|
|
||||||
if (!(*is_null))
|
|
||||||
{
|
|
||||||
const auto time_string = _handle->result.getCharPtrValue(_handle->count, index);
|
|
||||||
|
|
||||||
if (_handle->debug())
|
if (_handle->debug())
|
||||||
{
|
{
|
||||||
std::cerr << "PostgreSQL debug: got time string: " << time_string << std::endl;
|
std::cerr << "PostgreSQL debug: binding date_time result at index: " << index << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::regex rx {
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
|
||||||
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
if (!(*is_null))
|
||||||
};
|
{
|
||||||
std::cmatch mr;
|
const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index);
|
||||||
if (std::regex_match (time_string, mr, rx)) {
|
|
||||||
*value =
|
if (_handle->debug())
|
||||||
std::chrono::hours{std::atoi(time_string + mr.position(1))} + // Hour
|
{
|
||||||
std::chrono::minutes{std::atoi(time_string + mr.position(2))} + // Minute
|
std::cerr << "PostgreSQL debug: got date_time string: " << date_string << std::endl;
|
||||||
std::chrono::seconds{std::atoi(time_string + mr.position(3))} + // Second
|
}
|
||||||
::std::chrono::microseconds{ // Microsecond
|
|
||||||
mr[4].matched ? std::stoi((mr[4].str() + "000000").substr(0, 6)) : 0
|
static const std::regex rx {
|
||||||
};
|
"(\\d{4})-(\\d{2})-(\\d{2}) "
|
||||||
if (mr[5].matched) {
|
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
||||||
const auto tz_sign = (time_string[mr.position(5)] == '+') ? 1 : -1;
|
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
||||||
const auto tz_offset =
|
};
|
||||||
std::chrono::hours{std::atoi(time_string + mr.position(6))} +
|
std::cmatch mr;
|
||||||
std::chrono::minutes{mr[7].matched ? std::atoi(time_string + mr.position(7)) : 0} +
|
if (std::regex_match (date_string, mr, rx)) {
|
||||||
std::chrono::seconds{mr[8].matched ? std::atoi(time_string + mr.position(8)) : 0};
|
*value =
|
||||||
*value -= tz_sign * tz_offset;
|
::sqlpp::chrono::day_point{
|
||||||
}
|
::date::year{std::atoi(date_string + mr.position(1))} / // Year
|
||||||
} else {
|
std::atoi(date_string + mr.position(2)) / // Month
|
||||||
if (_handle->debug()) {
|
std::atoi(date_string + mr.position(3)) // Day of month
|
||||||
std::cerr << "PostgreSQL debug: got invalid time '" << time_string << "'" << std::endl;
|
} +
|
||||||
|
std::chrono::hours{std::atoi(date_string + mr.position(4))} + // Hour
|
||||||
|
std::chrono::minutes{std::atoi(date_string + mr.position(5))} + // Minute
|
||||||
|
std::chrono::seconds{std::atoi(date_string + mr.position(6))} + // Second
|
||||||
|
::std::chrono::microseconds{ // Microsecond
|
||||||
|
mr[7].matched ? std::stoi((mr[7].str() + "000000").substr(0, 6)) : 0
|
||||||
|
};
|
||||||
|
if (mr[8].matched) {
|
||||||
|
const auto tz_sign = (date_string[mr.position(8)] == '+') ? 1 : -1;
|
||||||
|
const auto tz_offset =
|
||||||
|
std::chrono::hours{std::atoi(date_string + mr.position(9))} +
|
||||||
|
std::chrono::minutes{mr[10].matched ? std::atoi(date_string + mr.position(10)) : 0} +
|
||||||
|
std::chrono::seconds{mr[11].matched ? std::atoi(date_string + mr.position(11)) : 0};
|
||||||
|
*value -= tz_sign * tz_offset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_handle->debug()) {
|
||||||
|
std::cerr << "PostgreSQL debug: got invalid date_time '" << date_string << "'" << std::endl;
|
||||||
|
}
|
||||||
|
*value = {};
|
||||||
}
|
}
|
||||||
*value = {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
inline void bind_result_t::_bind_blob_result(size_t _index, const uint8_t** value, size_t* len)
|
// always returns UTC time for time with time zone
|
||||||
{
|
void _bind_time_of_day_result(size_t _index, ::std::chrono::microseconds* value, bool* is_null)
|
||||||
|
|
||||||
auto index = static_cast<int>(_index);
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
{
|
||||||
std::cerr << "PostgreSQL debug: binding blob result at index: " << index << std::endl;
|
auto index = static_cast<int>(_index);
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding time result at index: " << index << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
|
|
||||||
|
if (!(*is_null))
|
||||||
|
{
|
||||||
|
const auto time_string = _handle->result.get_char_ptr_value(_handle->count, index);
|
||||||
|
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: got time string: " << time_string << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::regex rx {
|
||||||
|
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
||||||
|
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
||||||
|
};
|
||||||
|
std::cmatch mr;
|
||||||
|
if (std::regex_match (time_string, mr, rx)) {
|
||||||
|
*value =
|
||||||
|
std::chrono::hours{std::atoi(time_string + mr.position(1))} + // Hour
|
||||||
|
std::chrono::minutes{std::atoi(time_string + mr.position(2))} + // Minute
|
||||||
|
std::chrono::seconds{std::atoi(time_string + mr.position(3))} + // Second
|
||||||
|
::std::chrono::microseconds{ // Microsecond
|
||||||
|
mr[4].matched ? std::stoi((mr[4].str() + "000000").substr(0, 6)) : 0
|
||||||
|
};
|
||||||
|
if (mr[5].matched) {
|
||||||
|
const auto tz_sign = (time_string[mr.position(5)] == '+') ? 1 : -1;
|
||||||
|
const auto tz_offset =
|
||||||
|
std::chrono::hours{std::atoi(time_string + mr.position(6))} +
|
||||||
|
std::chrono::minutes{mr[7].matched ? std::atoi(time_string + mr.position(7)) : 0} +
|
||||||
|
std::chrono::seconds{mr[8].matched ? std::atoi(time_string + mr.position(8)) : 0};
|
||||||
|
*value -= tz_sign * tz_offset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_handle->debug()) {
|
||||||
|
std::cerr << "PostgreSQL debug: got invalid time '" << time_string << "'" << std::endl;
|
||||||
|
}
|
||||||
|
*value = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_handle->result.isNull(_handle->count, index))
|
void _bind_blob_result(size_t _index, const uint8_t** value, size_t* len)
|
||||||
{
|
{
|
||||||
*value = nullptr;
|
auto index = static_cast<int>(_index);
|
||||||
*len = 0;
|
if (_handle->debug())
|
||||||
}
|
{
|
||||||
else
|
std::cerr << "PostgreSQL debug: binding blob result at index: " << index << std::endl;
|
||||||
{
|
}
|
||||||
*value = _handle->result.getBlobValue(_handle->count, index);
|
|
||||||
*len = static_cast<size_t>(_handle->result.length(_handle->count, index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int bind_result_t::size() const
|
if (_handle->result.is_null(_handle->count, index))
|
||||||
{
|
{
|
||||||
return _handle->result.records_size();
|
*value = nullptr;
|
||||||
}
|
*len = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*value = _handle->result.get_blob_value(_handle->count, index);
|
||||||
|
*len = static_cast<size_t>(_handle->result.length(_handle->count, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() const
|
||||||
|
{
|
||||||
|
return _handle->result.records_size();
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace postgresql
|
} // namespace postgresql
|
||||||
} // namespace sqlpp
|
} // namespace sqlpp
|
||||||
|
@ -62,10 +62,9 @@ namespace sqlpp
|
|||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
// Forward declaration
|
|
||||||
inline std::unique_ptr<detail::prepared_statement_handle_t> prepare_statement(std::unique_ptr<connection_handle>& handle,
|
inline std::unique_ptr<detail::prepared_statement_handle_t> prepare_statement(std::unique_ptr<connection_handle>& handle,
|
||||||
const std::string& stmt,
|
const std::string& stmt,
|
||||||
const size_t& paramCount)
|
const size_t& param_count)
|
||||||
{
|
{
|
||||||
if (handle->config->debug)
|
if (handle->config->debug)
|
||||||
{
|
{
|
||||||
@ -73,7 +72,7 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
return std::unique_ptr<detail::prepared_statement_handle_t>(new detail::prepared_statement_handle_t
|
return std::unique_ptr<detail::prepared_statement_handle_t>(new detail::prepared_statement_handle_t
|
||||||
(*handle, stmt, paramCount));
|
(*handle, stmt, param_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void execute_prepared_statement(std::unique_ptr<connection_handle>& handle, std::shared_ptr<detail::prepared_statement_handle_t>& prepared)
|
inline void execute_prepared_statement(std::unique_ptr<connection_handle>& handle, std::shared_ptr<detail::prepared_statement_handle_t>& prepared)
|
||||||
@ -144,18 +143,67 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// direct execution
|
// direct execution
|
||||||
bind_result_t select_impl(const std::string& stmt);
|
bind_result_t select_impl(const std::string& stmt)
|
||||||
size_t insert_impl(const std::string& stmt);
|
{
|
||||||
size_t update_impl(const std::string& stmt);
|
return execute(stmt);
|
||||||
size_t remove_impl(const std::string& stmt);
|
}
|
||||||
|
|
||||||
|
size_t insert_impl(const std::string& stmt)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(execute(stmt)->result.affected_rows());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t update_impl(const std::string& stmt)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(execute(stmt)->result.affected_rows());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t remove_impl(const std::string& stmt)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(execute(stmt)->result.affected_rows());
|
||||||
|
}
|
||||||
|
|
||||||
// prepared execution
|
// prepared execution
|
||||||
prepared_statement_t prepare_impl(const std::string& stmt, const size_t& paramCount);
|
prepared_statement_t prepare_impl(const std::string& stmt, const size_t& param_count)
|
||||||
bind_result_t run_prepared_select_impl(prepared_statement_t& prep);
|
{
|
||||||
size_t run_prepared_execute_impl(prepared_statement_t& prep);
|
validate_connection_handle();
|
||||||
size_t run_prepared_insert_impl(prepared_statement_t& prep);
|
return {prepare_statement(_handle, stmt, param_count)};
|
||||||
size_t run_prepared_update_impl(prepared_statement_t& prep);
|
}
|
||||||
size_t run_prepared_remove_impl(prepared_statement_t& prep);
|
|
||||||
|
bind_result_t run_prepared_select_impl(prepared_statement_t& prep)
|
||||||
|
{
|
||||||
|
validate_connection_handle();
|
||||||
|
execute_prepared_statement(_handle, prep._handle);
|
||||||
|
return {prep._handle};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t run_prepared_execute_impl(prepared_statement_t& prep)
|
||||||
|
{
|
||||||
|
validate_connection_handle();
|
||||||
|
execute_prepared_statement(_handle, prep._handle);
|
||||||
|
return static_cast<size_t>(prep._handle->result.affected_rows());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t run_prepared_insert_impl(prepared_statement_t& prep)
|
||||||
|
{
|
||||||
|
validate_connection_handle();
|
||||||
|
execute_prepared_statement(_handle, prep._handle);
|
||||||
|
return static_cast<size_t>(prep._handle->result.affected_rows());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t run_prepared_update_impl(prepared_statement_t& prep)
|
||||||
|
{
|
||||||
|
validate_connection_handle();
|
||||||
|
execute_prepared_statement(_handle, prep._handle);
|
||||||
|
return static_cast<size_t>(prep._handle->result.affected_rows());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t run_prepared_remove_impl(prepared_statement_t& prep)
|
||||||
|
{
|
||||||
|
validate_connection_handle();
|
||||||
|
execute_prepared_statement(_handle, prep._handle);
|
||||||
|
return static_cast<size_t>(prep._handle->result.affected_rows());
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using _connection_base_t = connection_base;
|
using _connection_base_t = connection_base;
|
||||||
@ -284,7 +332,20 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
std::shared_ptr<detail::statement_handle_t> execute(const std::string& command);
|
std::shared_ptr<detail::statement_handle_t> execute(const std::string& stmt)
|
||||||
|
{
|
||||||
|
validate_connection_handle();
|
||||||
|
if (_handle->config->debug)
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: executing: " << stmt << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = std::make_shared<detail::statement_handle_t>(*_handle);
|
||||||
|
result->result = PQexec(native_handle(), stmt.c_str());
|
||||||
|
result->valid = true;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
typename Execute,
|
typename Execute,
|
||||||
@ -313,7 +374,19 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// escape argument
|
// escape argument
|
||||||
std::string escape(const std::string& s) const;
|
// TODO: Fix escaping.
|
||||||
|
std::string escape(const std::string& s) const
|
||||||
|
{
|
||||||
|
validate_connection_handle();
|
||||||
|
// Escape strings
|
||||||
|
std::string result;
|
||||||
|
result.resize((s.size() * 2) + 1);
|
||||||
|
|
||||||
|
int err;
|
||||||
|
size_t length = PQescapeStringConn(native_handle(), &result[0], s.c_str(), s.size(), &err);
|
||||||
|
result.resize(length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
//! call run on the argument
|
//! call run on the argument
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -348,37 +421,176 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
//! set the default transaction isolation level to use for new transactions
|
//! set the default transaction isolation level to use for new transactions
|
||||||
void set_default_isolation_level(isolation_level level);
|
void set_default_isolation_level(isolation_level level)
|
||||||
|
{
|
||||||
|
std::string level_str = "read uncommmitted";
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
/// @todo what about undefined ?
|
||||||
|
case isolation_level::read_committed:
|
||||||
|
level_str = "read committed";
|
||||||
|
break;
|
||||||
|
case isolation_level::read_uncommitted:
|
||||||
|
level_str = "read uncommitted";
|
||||||
|
break;
|
||||||
|
case isolation_level::repeatable_read:
|
||||||
|
level_str = "repeatable read";
|
||||||
|
break;
|
||||||
|
case isolation_level::serializable:
|
||||||
|
level_str = "serializable";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw sqlpp::exception("Invalid isolation level");
|
||||||
|
}
|
||||||
|
std::string cmd = "SET default_transaction_isolation to '" + level_str + "'";
|
||||||
|
execute(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
//! get the currently set default transaction isolation level
|
//! get the currently set default transaction isolation level
|
||||||
isolation_level get_default_isolation_level();
|
isolation_level get_default_isolation_level()
|
||||||
|
{
|
||||||
|
auto res = execute("SHOW default_transaction_isolation;");
|
||||||
|
auto status = res->result.status();
|
||||||
|
if ((status != PGRES_TUPLES_OK) && (status != PGRES_COMMAND_OK))
|
||||||
|
{
|
||||||
|
throw sqlpp::exception("PostgreSQL error: could not read default_transaction_isolation");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto in = res->result.get_string_value(0, 0);
|
||||||
|
if (in == "read committed")
|
||||||
|
{
|
||||||
|
return isolation_level::read_committed;
|
||||||
|
}
|
||||||
|
else if (in == "read uncommitted")
|
||||||
|
{
|
||||||
|
return isolation_level::read_uncommitted;
|
||||||
|
}
|
||||||
|
else if (in == "repeatable read")
|
||||||
|
{
|
||||||
|
return isolation_level::repeatable_read;
|
||||||
|
}
|
||||||
|
else if (in == "serializable")
|
||||||
|
{
|
||||||
|
return isolation_level::serializable;
|
||||||
|
}
|
||||||
|
return isolation_level::undefined;
|
||||||
|
}
|
||||||
|
|
||||||
//! create savepoint
|
//! create savepoint
|
||||||
void savepoint(const std::string& name);
|
void savepoint(const std::string& name)
|
||||||
|
{
|
||||||
|
/// NOTE prevent from sql injection?
|
||||||
|
execute("SAVEPOINT " + name);
|
||||||
|
}
|
||||||
|
|
||||||
//! ROLLBACK TO SAVEPOINT
|
//! ROLLBACK TO SAVEPOINT
|
||||||
void rollback_to_savepoint(const std::string& name);
|
void rollback_to_savepoint(const std::string& name)
|
||||||
|
{
|
||||||
|
/// NOTE prevent from sql injection?
|
||||||
|
execute("ROLLBACK TO SAVEPOINT " + name);
|
||||||
|
}
|
||||||
|
|
||||||
//! release_savepoint
|
//! release_savepoint
|
||||||
void release_savepoint(const std::string& name);
|
void release_savepoint(const std::string& name)
|
||||||
|
{
|
||||||
|
/// NOTE prevent from sql injection?
|
||||||
|
execute("RELEASE SAVEPOINT " + name);
|
||||||
|
}
|
||||||
|
|
||||||
//! start transaction
|
//! start transaction
|
||||||
void start_transaction(isolation_level level = isolation_level::undefined);
|
void start_transaction(isolation_level level = isolation_level::undefined)
|
||||||
|
{
|
||||||
|
if (_transaction_active)
|
||||||
|
{
|
||||||
|
throw sqlpp::exception("PostgreSQL error: transaction already open");
|
||||||
|
}
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case isolation_level::serializable:
|
||||||
|
{
|
||||||
|
execute("BEGIN ISOLATION LEVEL SERIALIZABLE");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case isolation_level::repeatable_read:
|
||||||
|
{
|
||||||
|
execute("BEGIN ISOLATION LEVEL REPEATABLE READ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case isolation_level::read_committed:
|
||||||
|
{
|
||||||
|
execute("BEGIN ISOLATION LEVEL READ COMMITTED");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case isolation_level::read_uncommitted:
|
||||||
|
{
|
||||||
|
execute("BEGIN ISOLATION LEVEL READ UNCOMMITTED");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case isolation_level::undefined:
|
||||||
|
{
|
||||||
|
execute("BEGIN");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_transaction_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
//! commit transaction (or throw transaction if transaction has
|
//! commit transaction (or throw transaction if transaction has finished already)
|
||||||
// finished already)
|
void commit_transaction()
|
||||||
void commit_transaction();
|
{
|
||||||
|
if (!_transaction_active)
|
||||||
|
{
|
||||||
|
throw sqlpp::exception("PostgreSQL error: transaction failed or finished.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_transaction_active = false;
|
||||||
|
execute("COMMIT");
|
||||||
|
}
|
||||||
|
|
||||||
//! rollback transaction
|
//! rollback transaction
|
||||||
void rollback_transaction(bool report);
|
void rollback_transaction(bool report)
|
||||||
|
{
|
||||||
|
if (!_transaction_active)
|
||||||
|
{
|
||||||
|
throw sqlpp::exception("PostgreSQL error: transaction failed or finished.");
|
||||||
|
}
|
||||||
|
execute("ROLLBACK");
|
||||||
|
if (report)
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL warning: rolling back unfinished transaction" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
_transaction_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
//! report rollback failure
|
//! report rollback failure
|
||||||
void report_rollback_failure(const std::string& message) noexcept;
|
void report_rollback_failure(const std::string& message) noexcept
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL error: " << message << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
//! get the last inserted id for a certain table
|
//! get the last inserted id for a certain table
|
||||||
uint64_t last_insert_id(const std::string& table, const std::string& fieldname);
|
uint64_t last_insert_id(const std::string& table, const std::string& fieldname)
|
||||||
|
{
|
||||||
|
std::string sql = "SELECT currval('" + table + "_" + fieldname + "_seq')";
|
||||||
|
PGresult* res = PQexec(native_handle(), sql.c_str());
|
||||||
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
|
{
|
||||||
|
std::string err{PQresultErrorMessage(res)};
|
||||||
|
PQclear(res);
|
||||||
|
throw sqlpp::postgresql::undefined_table(err, sql);
|
||||||
|
}
|
||||||
|
|
||||||
::PGconn* native_handle() const;
|
// Parse the number and return.
|
||||||
|
std::string in{PQgetvalue(res, 0, 0)};
|
||||||
|
PQclear(res);
|
||||||
|
return std::stoul(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
::PGconn* native_handle() const
|
||||||
|
{
|
||||||
|
return _handle->native_handle();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
_handle_ptr_t _handle;
|
_handle_ptr_t _handle;
|
||||||
@ -390,268 +602,7 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::shared_ptr<detail::statement_handle_t> connection_base::execute(const std::string& stmt)
|
// Method definition moved outside of class because it needs connection_base
|
||||||
{
|
|
||||||
validate_connection_handle();
|
|
||||||
if (_handle->config->debug)
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: executing: " << stmt << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = std::make_shared<detail::statement_handle_t>(*_handle);
|
|
||||||
result->result = PQexec(native_handle(), stmt.c_str());
|
|
||||||
result->valid = true;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// direct execution
|
|
||||||
inline bind_result_t connection_base::select_impl(const std::string& stmt)
|
|
||||||
{
|
|
||||||
return execute(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t connection_base::insert_impl(const std::string& stmt)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(execute(stmt)->result.affected_rows());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t connection_base::update_impl(const std::string& stmt)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(execute(stmt)->result.affected_rows());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t connection_base::remove_impl(const std::string& stmt)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(execute(stmt)->result.affected_rows());
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepared execution
|
|
||||||
inline prepared_statement_t connection_base::prepare_impl(const std::string& stmt, const size_t& paramCount)
|
|
||||||
{
|
|
||||||
validate_connection_handle();
|
|
||||||
return {prepare_statement(_handle, stmt, paramCount)};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bind_result_t connection_base::run_prepared_select_impl(prepared_statement_t& prep)
|
|
||||||
{
|
|
||||||
validate_connection_handle();
|
|
||||||
execute_prepared_statement(_handle, prep._handle);
|
|
||||||
return {prep._handle};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t connection_base::run_prepared_execute_impl(prepared_statement_t& prep)
|
|
||||||
{
|
|
||||||
validate_connection_handle();
|
|
||||||
execute_prepared_statement(_handle, prep._handle);
|
|
||||||
return static_cast<size_t>(prep._handle->result.affected_rows());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t connection_base::run_prepared_insert_impl(prepared_statement_t& prep)
|
|
||||||
{
|
|
||||||
validate_connection_handle();
|
|
||||||
execute_prepared_statement(_handle, prep._handle);
|
|
||||||
return static_cast<size_t>(prep._handle->result.affected_rows());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t connection_base::run_prepared_update_impl(prepared_statement_t& prep)
|
|
||||||
{
|
|
||||||
validate_connection_handle();
|
|
||||||
execute_prepared_statement(_handle, prep._handle);
|
|
||||||
return static_cast<size_t>(prep._handle->result.affected_rows());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t connection_base::run_prepared_remove_impl(prepared_statement_t& prep)
|
|
||||||
{
|
|
||||||
validate_connection_handle();
|
|
||||||
execute_prepared_statement(_handle, prep._handle);
|
|
||||||
return static_cast<size_t>(prep._handle->result.affected_rows());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void connection_base::set_default_isolation_level(isolation_level level)
|
|
||||||
{
|
|
||||||
std::string level_str = "read uncommmitted";
|
|
||||||
switch (level)
|
|
||||||
{
|
|
||||||
/// @todo what about undefined ?
|
|
||||||
case isolation_level::read_committed:
|
|
||||||
level_str = "read committed";
|
|
||||||
break;
|
|
||||||
case isolation_level::read_uncommitted:
|
|
||||||
level_str = "read uncommitted";
|
|
||||||
break;
|
|
||||||
case isolation_level::repeatable_read:
|
|
||||||
level_str = "repeatable read";
|
|
||||||
break;
|
|
||||||
case isolation_level::serializable:
|
|
||||||
level_str = "serializable";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw sqlpp::exception("Invalid isolation level");
|
|
||||||
}
|
|
||||||
std::string cmd = "SET default_transaction_isolation to '" + level_str + "'";
|
|
||||||
execute(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! get the currently set default transaction isolation level
|
|
||||||
inline isolation_level connection_base::get_default_isolation_level()
|
|
||||||
{
|
|
||||||
auto res = execute("SHOW default_transaction_isolation;");
|
|
||||||
auto status = res->result.status();
|
|
||||||
if ((status != PGRES_TUPLES_OK) && (status != PGRES_COMMAND_OK))
|
|
||||||
{
|
|
||||||
throw sqlpp::exception("PostgreSQL error: could not read default_transaction_isolation");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto in = res->result.getStringValue(0, 0);
|
|
||||||
if (in == "read committed")
|
|
||||||
{
|
|
||||||
return isolation_level::read_committed;
|
|
||||||
}
|
|
||||||
else if (in == "read uncommitted")
|
|
||||||
{
|
|
||||||
return isolation_level::read_uncommitted;
|
|
||||||
}
|
|
||||||
else if (in == "repeatable read")
|
|
||||||
{
|
|
||||||
return isolation_level::repeatable_read;
|
|
||||||
}
|
|
||||||
else if (in == "serializable")
|
|
||||||
{
|
|
||||||
return isolation_level::serializable;
|
|
||||||
}
|
|
||||||
return isolation_level::undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Fix escaping.
|
|
||||||
inline std::string connection_base::escape(const std::string& s) const
|
|
||||||
{
|
|
||||||
validate_connection_handle();
|
|
||||||
// Escape strings
|
|
||||||
std::string result;
|
|
||||||
result.resize((s.size() * 2) + 1);
|
|
||||||
|
|
||||||
int err;
|
|
||||||
size_t length = PQescapeStringConn(native_handle(), &result[0], s.c_str(), s.size(), &err);
|
|
||||||
result.resize(length);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! start transaction
|
|
||||||
inline void connection_base::start_transaction(isolation_level level)
|
|
||||||
{
|
|
||||||
if (_transaction_active)
|
|
||||||
{
|
|
||||||
throw sqlpp::exception("PostgreSQL error: transaction already open");
|
|
||||||
}
|
|
||||||
switch (level)
|
|
||||||
{
|
|
||||||
case isolation_level::serializable:
|
|
||||||
{
|
|
||||||
execute("BEGIN ISOLATION LEVEL SERIALIZABLE");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case isolation_level::repeatable_read:
|
|
||||||
{
|
|
||||||
execute("BEGIN ISOLATION LEVEL REPEATABLE READ");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case isolation_level::read_committed:
|
|
||||||
{
|
|
||||||
execute("BEGIN ISOLATION LEVEL READ COMMITTED");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case isolation_level::read_uncommitted:
|
|
||||||
{
|
|
||||||
execute("BEGIN ISOLATION LEVEL READ UNCOMMITTED");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case isolation_level::undefined:
|
|
||||||
{
|
|
||||||
execute("BEGIN");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_transaction_active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! create savepoint
|
|
||||||
inline void connection_base::savepoint(const std::string& name)
|
|
||||||
{
|
|
||||||
/// NOTE prevent from sql injection?
|
|
||||||
execute("SAVEPOINT " + name);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! ROLLBACK TO SAVEPOINT
|
|
||||||
inline void connection_base::rollback_to_savepoint(const std::string& name)
|
|
||||||
{
|
|
||||||
/// NOTE prevent from sql injection?
|
|
||||||
execute("ROLLBACK TO SAVEPOINT " + name);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! release_savepoint
|
|
||||||
inline void connection_base::release_savepoint(const std::string& name)
|
|
||||||
{
|
|
||||||
/// NOTE prevent from sql injection?
|
|
||||||
execute("RELEASE SAVEPOINT " + name);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! commit transaction (or throw transaction if transaction has finished already)
|
|
||||||
inline void connection_base::commit_transaction()
|
|
||||||
{
|
|
||||||
if (!_transaction_active)
|
|
||||||
{
|
|
||||||
throw sqlpp::exception("PostgreSQL error: transaction failed or finished.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_transaction_active = false;
|
|
||||||
execute("COMMIT");
|
|
||||||
}
|
|
||||||
|
|
||||||
//! rollback transaction
|
|
||||||
inline void connection_base::rollback_transaction(bool report)
|
|
||||||
{
|
|
||||||
if (!_transaction_active)
|
|
||||||
{
|
|
||||||
throw sqlpp::exception("PostgreSQL error: transaction failed or finished.");
|
|
||||||
}
|
|
||||||
execute("ROLLBACK");
|
|
||||||
if (report)
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL warning: rolling back unfinished transaction" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
_transaction_active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! report rollback failure
|
|
||||||
inline void connection_base::report_rollback_failure(const std::string& message) noexcept
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL error: " << message << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint64_t connection_base::last_insert_id(const std::string& table, const std::string& fieldname)
|
|
||||||
{
|
|
||||||
std::string sql = "SELECT currval('" + table + "_" + fieldname + "_seq')";
|
|
||||||
PGresult* res = PQexec(native_handle(), sql.c_str());
|
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
std::string err{PQresultErrorMessage(res)};
|
|
||||||
PQclear(res);
|
|
||||||
throw sqlpp::postgresql::undefined_table(err, sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the number and return.
|
|
||||||
std::string in{PQgetvalue(res, 0, 0)};
|
|
||||||
PQclear(res);
|
|
||||||
return std::stoul(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ::PGconn* connection_base::native_handle() const
|
|
||||||
{
|
|
||||||
return _handle->native_handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string context_t::escape(const std::string& arg) const
|
inline std::string context_t::escape(const std::string& arg) const
|
||||||
{
|
{
|
||||||
return _db.escape(arg);
|
return _db.escape(arg);
|
||||||
|
@ -52,11 +52,6 @@ namespace sqlpp
|
|||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
inline void handle_cleanup(PGconn* postgres)
|
|
||||||
{
|
|
||||||
PQfinish(postgres);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DLL_LOCAL connection_handle
|
struct DLL_LOCAL connection_handle
|
||||||
{
|
{
|
||||||
std::shared_ptr<const connection_config> config;
|
std::shared_ptr<const connection_config> config;
|
||||||
@ -76,7 +71,7 @@ namespace sqlpp
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline connection_handle::connection_handle(const std::shared_ptr<const connection_config>& conf)
|
inline connection_handle::connection_handle(const std::shared_ptr<const connection_config>& conf)
|
||||||
: config(conf), postgres{nullptr, handle_cleanup}
|
: config(conf), postgres{nullptr, PQfinish}
|
||||||
{
|
{
|
||||||
#ifdef SQLPP_DYNAMIC_LOADING
|
#ifdef SQLPP_DYNAMIC_LOADING
|
||||||
init_pg("");
|
init_pg("");
|
||||||
|
@ -54,25 +54,45 @@ namespace sqlpp
|
|||||||
{
|
{
|
||||||
struct DLL_PUBLIC statement_handle_t
|
struct DLL_PUBLIC statement_handle_t
|
||||||
{
|
{
|
||||||
detail::connection_handle& connection;
|
connection_handle& connection;
|
||||||
Result result;
|
Result result;
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int totalCount = 0;
|
int total_count = 0;
|
||||||
int fields = 0;
|
int fields = 0;
|
||||||
|
|
||||||
// ctor
|
// ctor
|
||||||
statement_handle_t(detail::connection_handle& _connection);
|
statement_handle_t(connection_handle& _connection) : connection(_connection)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
statement_handle_t(const statement_handle_t&) = delete;
|
statement_handle_t(const statement_handle_t&) = delete;
|
||||||
statement_handle_t(statement_handle_t&&) = delete;
|
statement_handle_t(statement_handle_t&&) = delete;
|
||||||
statement_handle_t& operator=(const statement_handle_t&) = delete;
|
statement_handle_t& operator=(const statement_handle_t&) = delete;
|
||||||
statement_handle_t& operator=(statement_handle_t&&) = delete;
|
statement_handle_t& operator=(statement_handle_t&&) = delete;
|
||||||
|
|
||||||
virtual ~statement_handle_t();
|
virtual ~statement_handle_t()
|
||||||
bool operator!() const;
|
{
|
||||||
void clearResult();
|
clear_result();
|
||||||
|
}
|
||||||
|
|
||||||
bool debug() const;
|
bool operator!() const
|
||||||
|
{
|
||||||
|
return !valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_result()
|
||||||
|
{
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
result.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool debug() const
|
||||||
|
{
|
||||||
|
return connection.config->debug;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct prepared_statement_handle_t : public statement_handle_t
|
struct prepared_statement_handle_t : public statement_handle_t
|
||||||
@ -82,19 +102,47 @@ namespace sqlpp
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Store prepared statement arguments
|
// Store prepared statement arguments
|
||||||
std::vector<bool> nullValues;
|
std::vector<bool> null_values;
|
||||||
std::vector<std::string> paramValues;
|
std::vector<std::string> param_values;
|
||||||
|
|
||||||
// ctor
|
// ctor
|
||||||
prepared_statement_handle_t(detail::connection_handle& _connection, std::string stmt, const size_t& paramCount);
|
prepared_statement_handle_t(connection_handle& _connection, const std::string& stmt, const size_t& param_count)
|
||||||
|
: statement_handle_t(_connection), null_values(param_count), param_values(param_count)
|
||||||
|
{
|
||||||
|
generate_name();
|
||||||
|
prepare(std::move(stmt));
|
||||||
|
}
|
||||||
|
|
||||||
prepared_statement_handle_t(const prepared_statement_handle_t&) = delete;
|
prepared_statement_handle_t(const prepared_statement_handle_t&) = delete;
|
||||||
prepared_statement_handle_t(prepared_statement_handle_t&&) = delete;
|
prepared_statement_handle_t(prepared_statement_handle_t&&) = delete;
|
||||||
prepared_statement_handle_t& operator=(const prepared_statement_handle_t&) = delete;
|
prepared_statement_handle_t& operator=(const prepared_statement_handle_t&) = delete;
|
||||||
prepared_statement_handle_t& operator=(prepared_statement_handle_t&&) = delete;
|
prepared_statement_handle_t& operator=(prepared_statement_handle_t&&) = delete;
|
||||||
|
|
||||||
virtual ~prepared_statement_handle_t();
|
virtual ~prepared_statement_handle_t()
|
||||||
|
{
|
||||||
|
if (valid && !_name.empty())
|
||||||
|
{
|
||||||
|
connection.deallocate_prepared_statement(_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void execute();
|
void execute()
|
||||||
|
{
|
||||||
|
const size_t size = param_values.size();
|
||||||
|
|
||||||
|
std::vector<const char*> values;
|
||||||
|
for (size_t i = 0u; i < size; i++)
|
||||||
|
values.push_back(null_values[i] ? nullptr : const_cast<char*>(param_values[i].c_str()));
|
||||||
|
|
||||||
|
// Execute prepared statement with the parameters.
|
||||||
|
clear_result();
|
||||||
|
valid = false;
|
||||||
|
count = 0;
|
||||||
|
total_count = 0;
|
||||||
|
result = PQexecPrepared(connection.native_handle(), _name.data(), static_cast<int>(size), values.data(), nullptr, nullptr, 0);
|
||||||
|
/// @todo validate result? is it really valid
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string name() const
|
std::string name() const
|
||||||
{
|
{
|
||||||
@ -102,94 +150,29 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void generate_name();
|
void generate_name()
|
||||||
void prepare(std::string stmt);
|
{
|
||||||
|
// Generate a random name for the prepared statement
|
||||||
|
while (connection.prepared_statement_names.find(_name) != connection.prepared_statement_names.end())
|
||||||
|
{
|
||||||
|
std::generate_n(_name.begin(), 6, []() {
|
||||||
|
constexpr static auto charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz";
|
||||||
|
constexpr size_t max = (sizeof(charset) - 1);
|
||||||
|
std::random_device rd;
|
||||||
|
return charset[rd() % max];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
connection.prepared_statement_names.insert(_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepare(const std::string& stmt)
|
||||||
|
{
|
||||||
|
// Create the prepared statement
|
||||||
|
result = PQprepare(connection.native_handle(), _name.c_str(), stmt.c_str(), 0, nullptr);
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline statement_handle_t::statement_handle_t(connection_handle& _connection) : connection(_connection)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline statement_handle_t::~statement_handle_t()
|
|
||||||
{
|
|
||||||
clearResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool statement_handle_t::operator!() const
|
|
||||||
{
|
|
||||||
return !valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void statement_handle_t::clearResult()
|
|
||||||
{
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
result.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool statement_handle_t::debug() const
|
|
||||||
{
|
|
||||||
return connection.config->debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline prepared_statement_handle_t::prepared_statement_handle_t(connection_handle& _connection,
|
|
||||||
std::string stmt,
|
|
||||||
const size_t& paramCount)
|
|
||||||
: statement_handle_t(_connection), nullValues(paramCount), paramValues(paramCount)
|
|
||||||
{
|
|
||||||
generate_name();
|
|
||||||
prepare(std::move(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline prepared_statement_handle_t::~prepared_statement_handle_t()
|
|
||||||
{
|
|
||||||
if (valid && !_name.empty())
|
|
||||||
{
|
|
||||||
connection.deallocate_prepared_statement(_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_handle_t::execute()
|
|
||||||
{
|
|
||||||
const size_t size = paramValues.size();
|
|
||||||
|
|
||||||
std::vector<const char*> values;
|
|
||||||
for (size_t i = 0u; i < size; i++)
|
|
||||||
values.push_back(nullValues[i] ? nullptr : const_cast<char*>(paramValues[i].c_str()));
|
|
||||||
|
|
||||||
// Execute prepared statement with the parameters.
|
|
||||||
clearResult();
|
|
||||||
valid = false;
|
|
||||||
count = 0;
|
|
||||||
totalCount = 0;
|
|
||||||
result = PQexecPrepared(connection.native_handle(), _name.data(), static_cast<int>(size), values.data(), nullptr, nullptr, 0);
|
|
||||||
/// @todo validate result? is it really valid
|
|
||||||
valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_handle_t::generate_name()
|
|
||||||
{
|
|
||||||
// Generate a random name for the prepared statement
|
|
||||||
while (connection.prepared_statement_names.find(_name) != connection.prepared_statement_names.end())
|
|
||||||
{
|
|
||||||
std::generate_n(_name.begin(), 6, []() {
|
|
||||||
constexpr static auto charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
"abcdefghijklmnopqrstuvwxyz";
|
|
||||||
constexpr size_t max = (sizeof(charset) - 1);
|
|
||||||
std::random_device rd;
|
|
||||||
return charset[rd() % max];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
connection.prepared_statement_names.insert(_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_handle_t::prepare(std::string stmt)
|
|
||||||
{
|
|
||||||
// Create the prepared statement
|
|
||||||
result = PQprepare(connection.native_handle(), _name.c_str(), stmt.c_str(), 0, nullptr);
|
|
||||||
valid = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,18 @@ namespace sqlpp
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
prepared_statement_t() = default;
|
prepared_statement_t() = default;
|
||||||
prepared_statement_t(std::shared_ptr<detail::prepared_statement_handle_t>&& handle);
|
|
||||||
|
// ctor
|
||||||
|
prepared_statement_t(std::shared_ptr<detail::prepared_statement_handle_t>&& handle)
|
||||||
|
: _handle{handle}
|
||||||
|
{
|
||||||
|
if (_handle && _handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: constructing prepared_statement, using handle at: " << _handle.get()
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
prepared_statement_t(const prepared_statement_t&) = delete;
|
prepared_statement_t(const prepared_statement_t&) = delete;
|
||||||
prepared_statement_t(prepared_statement_t&&) = default;
|
prepared_statement_t(prepared_statement_t&&) = default;
|
||||||
prepared_statement_t& operator=(const prepared_statement_t&) = delete;
|
prepared_statement_t& operator=(const prepared_statement_t&) = delete;
|
||||||
@ -72,194 +83,174 @@ namespace sqlpp
|
|||||||
return (this->_handle == rhs._handle);
|
return (this->_handle == rhs._handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bind_boolean_parameter(size_t index, const signed char* value, bool is_null);
|
void _bind_boolean_parameter(size_t index, const signed char* value, bool is_null)
|
||||||
void _bind_floating_point_parameter(size_t index, const double* value, bool is_null);
|
{
|
||||||
void _bind_integral_parameter(size_t index, const int64_t* value, bool is_null);
|
if (_handle->debug())
|
||||||
void _bind_text_parameter(size_t index, const std::string* value, bool is_null);
|
{
|
||||||
void _bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null);
|
std::cerr << "PostgreSQL debug: binding boolean parameter " << (*value ? "true" : "false")
|
||||||
void _bind_time_of_day_parameter(size_t index, const ::std::chrono::microseconds* value, bool is_null);
|
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||||
void _bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null);
|
}
|
||||||
void _bind_blob_parameter(size_t index, const std::vector<unsigned char>* value, bool is_null);
|
|
||||||
|
_handle->null_values[index] = is_null;
|
||||||
|
if (!is_null)
|
||||||
|
{
|
||||||
|
if (*value)
|
||||||
|
{
|
||||||
|
_handle->param_values[index] = "TRUE";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_handle->param_values[index] = "FALSE";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_floating_point_parameter(size_t index, const double* value, bool is_null)
|
||||||
|
{
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding floating_point parameter " << *value << " at index: " << index
|
||||||
|
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handle->null_values[index] = is_null;
|
||||||
|
if (!is_null)
|
||||||
|
{
|
||||||
|
sqlpp::detail::float_safe_ostringstream out;
|
||||||
|
out << *value;
|
||||||
|
_handle->param_values[index] = out.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_integral_parameter(size_t index, const int64_t* value, bool is_null)
|
||||||
|
{
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding integral parameter " << *value << " at index: " << index << ", being "
|
||||||
|
<< (is_null ? "" : "not ") << "null" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign values
|
||||||
|
_handle->null_values[index] = is_null;
|
||||||
|
if (!is_null)
|
||||||
|
{
|
||||||
|
_handle->param_values[index] = std::to_string(*value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_text_parameter(size_t index, const std::string* value, bool is_null)
|
||||||
|
{
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding text parameter " << *value << " at index: " << index << ", being "
|
||||||
|
<< (is_null ? "" : "not ") << "null" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign values
|
||||||
|
_handle->null_values[index] = is_null;
|
||||||
|
if (!is_null)
|
||||||
|
{
|
||||||
|
_handle->param_values[index] = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null)
|
||||||
|
{
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding date parameter at index "
|
||||||
|
<< index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||||
|
}
|
||||||
|
_handle->null_values[index] = is_null;
|
||||||
|
if (not is_null)
|
||||||
|
{
|
||||||
|
const auto ymd = ::date::year_month_day{*value};
|
||||||
|
std::ostringstream os;
|
||||||
|
os << ymd;
|
||||||
|
_handle->param_values[index] = os.str();
|
||||||
|
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding date parameter string: " << _handle->param_values[index] << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_time_of_day_parameter(size_t index, const ::std::chrono::microseconds* value, bool is_null)
|
||||||
|
{
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding time parameter at index "
|
||||||
|
<< index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||||
|
}
|
||||||
|
_handle->null_values[index] = is_null;
|
||||||
|
if (not is_null)
|
||||||
|
{
|
||||||
|
const auto time = ::date::make_time(*value) ;
|
||||||
|
|
||||||
|
// Timezone handling - always treat the local value as UTC.
|
||||||
|
std::ostringstream os;
|
||||||
|
os << time << "+00";
|
||||||
|
_handle->param_values[index] = os.str();
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding time parameter string: " << _handle->param_values[index] << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null)
|
||||||
|
{
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding date_time parameter at index "
|
||||||
|
<< index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||||
|
}
|
||||||
|
_handle->null_values[index] = is_null;
|
||||||
|
if (not is_null)
|
||||||
|
{
|
||||||
|
const auto dp = ::sqlpp::chrono::floor<::date::days>(*value);
|
||||||
|
const auto time = ::date::make_time(::sqlpp::chrono::floor<::std::chrono::microseconds>(*value - dp));
|
||||||
|
const auto ymd = ::date::year_month_day{dp};
|
||||||
|
|
||||||
|
// Timezone handling - always treat the local value as UTC.
|
||||||
|
std::ostringstream os;
|
||||||
|
os << ymd << ' ' << time << "+00";
|
||||||
|
_handle->param_values[index] = os.str();
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding date_time parameter string: " << _handle->param_values[index] << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _bind_blob_parameter(size_t index, const std::vector<unsigned char>* value, bool is_null)
|
||||||
|
{
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding blob parameter at index "
|
||||||
|
<< index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||||
|
}
|
||||||
|
_handle->null_values[index] = is_null;
|
||||||
|
if (not is_null)
|
||||||
|
{
|
||||||
|
constexpr char hex_chars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||||
|
auto param = std::string(value->size() * 2 + 2, '\0');
|
||||||
|
param[0] = '\\';
|
||||||
|
param[1] = 'x';
|
||||||
|
auto i = size_t{1};
|
||||||
|
for (const auto c : *value)
|
||||||
|
{
|
||||||
|
param[++i] = hex_chars[c >> 4];
|
||||||
|
param[++i] = hex_chars[c & 0x0F];
|
||||||
|
}
|
||||||
|
_handle->param_values[index] = std::move(param);
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: binding blob parameter string (up to 100 chars): " << _handle->param_values[index].substr(0, 100) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ctor
|
|
||||||
inline prepared_statement_t::prepared_statement_t(std::shared_ptr<detail::prepared_statement_handle_t>&& handle)
|
|
||||||
: _handle{handle}
|
|
||||||
{
|
|
||||||
if (_handle && _handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: constructing prepared_statement, using handle at: " << _handle.get()
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_t::_bind_boolean_parameter(size_t index, const signed char* value, bool is_null)
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding boolean parameter " << (*value ? "true" : "false")
|
|
||||||
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
_handle->nullValues[index] = is_null;
|
|
||||||
if (!is_null)
|
|
||||||
{
|
|
||||||
if (*value)
|
|
||||||
{
|
|
||||||
_handle->paramValues[index] = "TRUE";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_handle->paramValues[index] = "FALSE";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_t::_bind_floating_point_parameter(size_t index, const double* value, bool is_null)
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding floating_point parameter " << *value << " at index: " << index
|
|
||||||
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
_handle->nullValues[index] = is_null;
|
|
||||||
if (!is_null)
|
|
||||||
{
|
|
||||||
sqlpp::detail::float_safe_ostringstream out;
|
|
||||||
out << *value;
|
|
||||||
_handle->paramValues[index] = out.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_t::_bind_integral_parameter(size_t index, const int64_t* value, bool is_null)
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding integral parameter " << *value << " at index: " << index << ", being "
|
|
||||||
<< (is_null ? "" : "not ") << "null" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign values
|
|
||||||
_handle->nullValues[index] = is_null;
|
|
||||||
if (!is_null)
|
|
||||||
{
|
|
||||||
_handle->paramValues[index] = std::to_string(*value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_t::_bind_text_parameter(size_t index, const std::string* value, bool is_null)
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding text parameter " << *value << " at index: " << index << ", being "
|
|
||||||
<< (is_null ? "" : "not ") << "null" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign values
|
|
||||||
_handle->nullValues[index] = is_null;
|
|
||||||
if (!is_null)
|
|
||||||
{
|
|
||||||
_handle->paramValues[index] = *value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_t::_bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null)
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding date parameter at index "
|
|
||||||
<< index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
|
||||||
}
|
|
||||||
_handle->nullValues[index] = is_null;
|
|
||||||
if (not is_null)
|
|
||||||
{
|
|
||||||
const auto ymd = ::date::year_month_day{*value};
|
|
||||||
std::ostringstream os;
|
|
||||||
os << ymd;
|
|
||||||
_handle->paramValues[index] = os.str();
|
|
||||||
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding date parameter string: " << _handle->paramValues[index] << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_t::_bind_time_of_day_parameter(size_t index, const ::std::chrono::microseconds* value, bool is_null)
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding time parameter at index "
|
|
||||||
<< index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
|
||||||
}
|
|
||||||
_handle->nullValues[index] = is_null;
|
|
||||||
if (not is_null)
|
|
||||||
{
|
|
||||||
const auto time = ::date::make_time(*value) ;
|
|
||||||
|
|
||||||
// Timezone handling - always treat the local value as UTC.
|
|
||||||
std::ostringstream os;
|
|
||||||
os << time << "+00";
|
|
||||||
_handle->paramValues[index] = os.str();
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding time parameter string: " << _handle->paramValues[index] << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_t::_bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null)
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding date_time parameter at index "
|
|
||||||
<< index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
|
||||||
}
|
|
||||||
_handle->nullValues[index] = is_null;
|
|
||||||
if (not is_null)
|
|
||||||
{
|
|
||||||
const auto dp = ::sqlpp::chrono::floor<::date::days>(*value);
|
|
||||||
const auto time = ::date::make_time(::sqlpp::chrono::floor<::std::chrono::microseconds>(*value - dp));
|
|
||||||
const auto ymd = ::date::year_month_day{dp};
|
|
||||||
|
|
||||||
// Timezone handling - always treat the local value as UTC.
|
|
||||||
std::ostringstream os;
|
|
||||||
os << ymd << ' ' << time << "+00";
|
|
||||||
_handle->paramValues[index] = os.str();
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding date_time parameter string: " << _handle->paramValues[index] << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void prepared_statement_t::_bind_blob_parameter(size_t index, const std::vector<unsigned char>* value, bool is_null)
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding blob parameter at index "
|
|
||||||
<< index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
|
||||||
}
|
|
||||||
_handle->nullValues[index] = is_null;
|
|
||||||
if (not is_null)
|
|
||||||
{
|
|
||||||
constexpr char hexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
|
||||||
auto param = std::string(value->size() * 2 + 2, '\0');
|
|
||||||
param[0] = '\\';
|
|
||||||
param[1] = 'x';
|
|
||||||
auto i = size_t{1};
|
|
||||||
for (const auto c : *value)
|
|
||||||
{
|
|
||||||
param[++i] = hexChars[c >> 4];
|
|
||||||
param[++i] = hexChars[c & 0x0F];
|
|
||||||
}
|
|
||||||
_handle->paramValues[index] = std::move(param);
|
|
||||||
if (_handle->debug())
|
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: binding blob parameter string (up to 100 chars): " << _handle->paramValues[index].substr(0, 100) << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,26 +53,71 @@ namespace sqlpp
|
|||||||
class DLL_PUBLIC Result
|
class DLL_PUBLIC Result
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Result();
|
Result() : m_result(nullptr)
|
||||||
~Result();
|
|
||||||
|
|
||||||
ExecStatusType status();
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
int affected_rows();
|
|
||||||
int records_size() const;
|
|
||||||
int field_count() const;
|
|
||||||
int length(int record, int field) const;
|
|
||||||
bool isNull(int record, int field) const;
|
|
||||||
void operator=(PGresult* res);
|
|
||||||
operator bool() const;
|
|
||||||
|
|
||||||
inline int64_t getInt64Value(int record, int field) const
|
|
||||||
{
|
{
|
||||||
checkIndex(record, field);
|
}
|
||||||
|
|
||||||
|
~Result()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecStatusType status()
|
||||||
|
{
|
||||||
|
return PQresultStatus(m_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if (m_result)
|
||||||
|
PQclear(m_result);
|
||||||
|
m_result = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int affected_rows()
|
||||||
|
{
|
||||||
|
const char* const rows_str = PQcmdTuples(m_result);
|
||||||
|
return rows_str[0] ? std::stoi(std::string(rows_str)) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int records_size() const
|
||||||
|
{
|
||||||
|
return m_result ? PQntuples(m_result) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int field_count() const
|
||||||
|
{
|
||||||
|
return m_result ? PQnfields(m_result) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length(int record, int field) const
|
||||||
|
{
|
||||||
|
/// check index?
|
||||||
|
return PQgetlength(m_result, record, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_null(int record, int field) const
|
||||||
|
{
|
||||||
|
/// check index?
|
||||||
|
return PQgetisnull(m_result, record, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(PGresult* res)
|
||||||
|
{
|
||||||
|
m_result = res;
|
||||||
|
check_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return m_result != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_int64_value(int record, int field) const
|
||||||
|
{
|
||||||
|
check_index(record, field);
|
||||||
auto t = int64_t{};
|
auto t = int64_t{};
|
||||||
const auto txt = std::string(getPqValue(m_result, record, field));
|
const auto txt = std::string(get_pq_value(m_result, record, field));
|
||||||
if(txt != "")
|
if(txt != "")
|
||||||
{
|
{
|
||||||
t = std::stoll(txt);
|
t = std::stoll(txt);
|
||||||
@ -81,11 +126,11 @@ namespace sqlpp
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t getUInt64Value(int record, int field) const
|
uint64_t get_uint64_value(int record, int field) const
|
||||||
{
|
{
|
||||||
checkIndex(record, field);
|
check_index(record, field);
|
||||||
auto t = uint64_t{};
|
auto t = uint64_t{};
|
||||||
const auto txt = std::string(getPqValue(m_result, record, field));
|
const auto txt = std::string(get_pq_value(m_result, record, field));
|
||||||
if(txt != "")
|
if(txt != "")
|
||||||
{
|
{
|
||||||
t = std::stoull(txt);
|
t = std::stoull(txt);
|
||||||
@ -94,11 +139,11 @@ namespace sqlpp
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double getDoubleValue(int record, int field) const
|
double get_double_value(int record, int field) const
|
||||||
{
|
{
|
||||||
checkIndex(record, field);
|
check_index(record, field);
|
||||||
auto t = double{};
|
auto t = double{};
|
||||||
auto txt = std::string(getPqValue(m_result, record, field));
|
auto txt = std::string(get_pq_value(m_result, record, field));
|
||||||
if(txt != "")
|
if(txt != "")
|
||||||
{
|
{
|
||||||
t = std::stod(txt);
|
t = std::stod(txt);
|
||||||
@ -107,25 +152,25 @@ namespace sqlpp
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char* getCharPtrValue(int record, int field) const
|
const char* get_char_ptr_value(int record, int field) const
|
||||||
{
|
{
|
||||||
return const_cast<const char*>(getPqValue(m_result, record, field));
|
return const_cast<const char*>(get_pq_value(m_result, record, field));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string getStringValue(int record, int field) const
|
std::string get_string_value(int record, int field) const
|
||||||
{
|
{
|
||||||
return {getCharPtrValue(record, field)};
|
return {get_char_ptr_value(record, field)};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const uint8_t* getBlobValue(int record, int field) const
|
const uint8_t* get_blob_value(int record, int field) const
|
||||||
{
|
{
|
||||||
return reinterpret_cast<const uint8_t*>(getPqValue(m_result, record, field));
|
return reinterpret_cast<const uint8_t*>(get_pq_value(m_result, record, field));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool getBoolValue(int record, int field) const
|
bool get_bool_value(int record, int field) const
|
||||||
{
|
{
|
||||||
checkIndex(record, field);
|
check_index(record, field);
|
||||||
auto val = getPqValue(m_result, record, field);
|
auto val = get_pq_value(m_result, record, field);
|
||||||
if (*val == 't')
|
if (*val == 't')
|
||||||
return true;
|
return true;
|
||||||
else if (*val == 'f')
|
else if (*val == 'f')
|
||||||
@ -144,239 +189,169 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CheckStatus() const;
|
void check_status() const
|
||||||
[[noreturn]] void ThrowSQLError(const std::string& Err, const std::string& Query) const;
|
{
|
||||||
std::string StatusError() const;
|
const std::string err = status_error();
|
||||||
int errorPosition() const noexcept;
|
if (!err.empty())
|
||||||
bool hasError();
|
throw_sql_error(err, query());
|
||||||
void checkIndex(int record, int field) const noexcept(false);
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void throw_sql_error(const std::string& err, const std::string& query) const
|
||||||
|
{
|
||||||
|
// Try to establish more precise error type, and throw corresponding exception
|
||||||
|
const char* const code = PQresultErrorField(m_result, PG_DIAG_SQLSTATE);
|
||||||
|
if (code)
|
||||||
|
switch (code[0])
|
||||||
|
{
|
||||||
|
case '0':
|
||||||
|
switch (code[1])
|
||||||
|
{
|
||||||
|
case '8':
|
||||||
|
throw broken_connection(err);
|
||||||
|
case 'A':
|
||||||
|
throw feature_not_supported(err, query);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
switch (code[1])
|
||||||
|
{
|
||||||
|
case '2':
|
||||||
|
throw data_exception(err, query);
|
||||||
|
case '3':
|
||||||
|
if (strcmp(code, "23001") == 0)
|
||||||
|
throw restrict_violation(err, query);
|
||||||
|
if (strcmp(code, "23502") == 0)
|
||||||
|
throw not_null_violation(err, query);
|
||||||
|
if (strcmp(code, "23503") == 0)
|
||||||
|
throw foreign_key_violation(err, query);
|
||||||
|
if (strcmp(code, "23505") == 0)
|
||||||
|
throw unique_violation(err, query);
|
||||||
|
if (strcmp(code, "23514") == 0)
|
||||||
|
throw check_violation(err, query);
|
||||||
|
throw integrity_constraint_violation(err, query);
|
||||||
|
case '4':
|
||||||
|
throw invalid_cursor_state(err, query);
|
||||||
|
case '6':
|
||||||
|
throw invalid_sql_statement_name(err, query);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
switch (code[1])
|
||||||
|
{
|
||||||
|
case '4':
|
||||||
|
throw invalid_cursor_name(err, query);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
switch (code[1])
|
||||||
|
{
|
||||||
|
case '2':
|
||||||
|
if (strcmp(code, "42501") == 0)
|
||||||
|
throw insufficient_privilege(err, query);
|
||||||
|
if (strcmp(code, "42601") == 0)
|
||||||
|
throw syntax_error(err, query, error_position());
|
||||||
|
if (strcmp(code, "42703") == 0)
|
||||||
|
throw undefined_column(err, query);
|
||||||
|
if (strcmp(code, "42883") == 0)
|
||||||
|
throw undefined_function(err, query);
|
||||||
|
if (strcmp(code, "42P01") == 0)
|
||||||
|
throw undefined_table(err, query);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '5':
|
||||||
|
switch (code[1])
|
||||||
|
{
|
||||||
|
case '3':
|
||||||
|
if (strcmp(code, "53100") == 0)
|
||||||
|
throw disk_full(err, query);
|
||||||
|
if (strcmp(code, "53200") == 0)
|
||||||
|
throw out_of_memory(err, query);
|
||||||
|
if (strcmp(code, "53300") == 0)
|
||||||
|
throw too_many_connections(err);
|
||||||
|
throw insufficient_resources(err, query);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'P':
|
||||||
|
if (strcmp(code, "P0001") == 0)
|
||||||
|
throw plpgsql_raise(err, query);
|
||||||
|
if (strcmp(code, "P0002") == 0)
|
||||||
|
throw plpgsql_no_data_found(err, query);
|
||||||
|
if (strcmp(code, "P0003") == 0)
|
||||||
|
throw plpgsql_too_many_rows(err, query);
|
||||||
|
throw plpgsql_error(err, query);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw sql_user_error(err, query, code);
|
||||||
|
}
|
||||||
|
throw sql_error(err, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string status_error() const
|
||||||
|
{
|
||||||
|
if (!m_result)
|
||||||
|
throw failure("No result set given");
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
|
||||||
|
switch (PQresultStatus(m_result))
|
||||||
|
{
|
||||||
|
case PGRES_EMPTY_QUERY: // The string sent to the backend was empty.
|
||||||
|
case PGRES_COMMAND_OK: // Successful completion of a command returning no data
|
||||||
|
case PGRES_TUPLES_OK: // The query successfully executed
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGRES_COPY_OUT: // Copy Out (from server) data transfer started
|
||||||
|
case PGRES_COPY_IN: // Copy In (to server) data transfer started
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGRES_BAD_RESPONSE: // The server's response was not understood
|
||||||
|
case PGRES_NONFATAL_ERROR:
|
||||||
|
case PGRES_FATAL_ERROR:
|
||||||
|
err = PQresultErrorMessage(m_result);
|
||||||
|
break;
|
||||||
|
#if PG_MAJORVERSION_NUM >= 13
|
||||||
|
case PGRES_COPY_BOTH:
|
||||||
|
case PGRES_SINGLE_TUPLE:
|
||||||
|
#endif
|
||||||
|
#if PG_MAJORVERSION_NUM >= 14
|
||||||
|
case PGRES_PIPELINE_SYNC:
|
||||||
|
case PGRES_PIPELINE_ABORTED:
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
throw sqlpp::exception("pqxx::result: Unrecognized response code " +
|
||||||
|
std::to_string(PQresultStatus(m_result)));
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int error_position() const noexcept
|
||||||
|
{
|
||||||
|
int pos = -1;
|
||||||
|
if (m_result)
|
||||||
|
{
|
||||||
|
const char* p = PQresultErrorField(m_result, PG_DIAG_STATEMENT_POSITION);
|
||||||
|
if (p)
|
||||||
|
pos = std::stoi(std::string(p));
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_index(int record, int field) const noexcept(false)
|
||||||
|
{
|
||||||
|
if (record > records_size() || field > field_count())
|
||||||
|
throw std::out_of_range("PostgreSQL error: index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
// move PQgetvalue to implementation so we don't depend on the libpq in the
|
// move PQgetvalue to implementation so we don't depend on the libpq in the
|
||||||
// public interface
|
// public interface
|
||||||
const char* getPqValue(PGresult* result, int record, int field) const;
|
const char* get_pq_value(PGresult* result, int record, int field) const
|
||||||
|
{
|
||||||
|
return const_cast<const char*>(PQgetvalue(result, record, field));
|
||||||
|
}
|
||||||
|
|
||||||
PGresult* m_result;
|
PGresult* m_result;
|
||||||
std::string m_query;
|
std::string m_query;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
inline Result::Result() : m_result(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Result::checkIndex(int record, int field) const noexcept(false)
|
|
||||||
{
|
|
||||||
if (record > records_size() || field > field_count())
|
|
||||||
throw std::out_of_range("PostgreSQL error: index out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Result::operator=(PGresult* res)
|
|
||||||
{
|
|
||||||
m_result = res;
|
|
||||||
CheckStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Result::CheckStatus() const
|
|
||||||
{
|
|
||||||
const std::string Err = StatusError();
|
|
||||||
if (!Err.empty())
|
|
||||||
ThrowSQLError(Err, query());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* Result::getPqValue(PGresult* result, int record, int field) const
|
|
||||||
{
|
|
||||||
return const_cast<const char*>(PQgetvalue(result, record, field));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] inline void Result::ThrowSQLError(const std::string& Err, const std::string& Query) const
|
|
||||||
{
|
|
||||||
// Try to establish more precise error type, and throw corresponding exception
|
|
||||||
const char* const code = PQresultErrorField(m_result, PG_DIAG_SQLSTATE);
|
|
||||||
if (code)
|
|
||||||
switch (code[0])
|
|
||||||
{
|
|
||||||
case '0':
|
|
||||||
switch (code[1])
|
|
||||||
{
|
|
||||||
case '8':
|
|
||||||
throw broken_connection(Err);
|
|
||||||
case 'A':
|
|
||||||
throw feature_not_supported(Err, Query);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '2':
|
|
||||||
switch (code[1])
|
|
||||||
{
|
|
||||||
case '2':
|
|
||||||
throw data_exception(Err, Query);
|
|
||||||
case '3':
|
|
||||||
if (strcmp(code, "23001") == 0)
|
|
||||||
throw restrict_violation(Err, Query);
|
|
||||||
if (strcmp(code, "23502") == 0)
|
|
||||||
throw not_null_violation(Err, Query);
|
|
||||||
if (strcmp(code, "23503") == 0)
|
|
||||||
throw foreign_key_violation(Err, Query);
|
|
||||||
if (strcmp(code, "23505") == 0)
|
|
||||||
throw unique_violation(Err, Query);
|
|
||||||
if (strcmp(code, "23514") == 0)
|
|
||||||
throw check_violation(Err, Query);
|
|
||||||
throw integrity_constraint_violation(Err, Query);
|
|
||||||
case '4':
|
|
||||||
throw invalid_cursor_state(Err, Query);
|
|
||||||
case '6':
|
|
||||||
throw invalid_sql_statement_name(Err, Query);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '3':
|
|
||||||
switch (code[1])
|
|
||||||
{
|
|
||||||
case '4':
|
|
||||||
throw invalid_cursor_name(Err, Query);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '4':
|
|
||||||
switch (code[1])
|
|
||||||
{
|
|
||||||
case '2':
|
|
||||||
if (strcmp(code, "42501") == 0)
|
|
||||||
throw insufficient_privilege(Err, Query);
|
|
||||||
if (strcmp(code, "42601") == 0)
|
|
||||||
throw syntax_error(Err, Query, errorPosition());
|
|
||||||
if (strcmp(code, "42703") == 0)
|
|
||||||
throw undefined_column(Err, Query);
|
|
||||||
if (strcmp(code, "42883") == 0)
|
|
||||||
throw undefined_function(Err, Query);
|
|
||||||
if (strcmp(code, "42P01") == 0)
|
|
||||||
throw undefined_table(Err, Query);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '5':
|
|
||||||
switch (code[1])
|
|
||||||
{
|
|
||||||
case '3':
|
|
||||||
if (strcmp(code, "53100") == 0)
|
|
||||||
throw disk_full(Err, Query);
|
|
||||||
if (strcmp(code, "53200") == 0)
|
|
||||||
throw out_of_memory(Err, Query);
|
|
||||||
if (strcmp(code, "53300") == 0)
|
|
||||||
throw too_many_connections(Err);
|
|
||||||
throw insufficient_resources(Err, Query);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'P':
|
|
||||||
if (strcmp(code, "P0001") == 0)
|
|
||||||
throw plpgsql_raise(Err, Query);
|
|
||||||
if (strcmp(code, "P0002") == 0)
|
|
||||||
throw plpgsql_no_data_found(Err, Query);
|
|
||||||
if (strcmp(code, "P0003") == 0)
|
|
||||||
throw plpgsql_too_many_rows(Err, Query);
|
|
||||||
throw plpgsql_error(Err, Query);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw sql_user_error(Err, Query, code);
|
|
||||||
}
|
|
||||||
throw sql_error(Err, Query);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string Result::StatusError() const
|
|
||||||
{
|
|
||||||
if (!m_result)
|
|
||||||
throw failure("No result set given");
|
|
||||||
|
|
||||||
std::string Err;
|
|
||||||
|
|
||||||
switch (PQresultStatus(m_result))
|
|
||||||
{
|
|
||||||
case PGRES_EMPTY_QUERY: // The string sent to the backend was empty.
|
|
||||||
case PGRES_COMMAND_OK: // Successful completion of a command returning no data
|
|
||||||
case PGRES_TUPLES_OK: // The query successfully executed
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PGRES_COPY_OUT: // Copy Out (from server) data transfer started
|
|
||||||
case PGRES_COPY_IN: // Copy In (to server) data transfer started
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PGRES_BAD_RESPONSE: // The server's response was not understood
|
|
||||||
case PGRES_NONFATAL_ERROR:
|
|
||||||
case PGRES_FATAL_ERROR:
|
|
||||||
Err = PQresultErrorMessage(m_result);
|
|
||||||
break;
|
|
||||||
#if PG_MAJORVERSION_NUM >= 13
|
|
||||||
case PGRES_COPY_BOTH:
|
|
||||||
case PGRES_SINGLE_TUPLE:
|
|
||||||
#endif
|
|
||||||
#if PG_MAJORVERSION_NUM >= 14
|
|
||||||
case PGRES_PIPELINE_SYNC:
|
|
||||||
case PGRES_PIPELINE_ABORTED:
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
throw sqlpp::exception("pqxx::result: Unrecognized response code " +
|
|
||||||
std::to_string(PQresultStatus(m_result)));
|
|
||||||
}
|
|
||||||
return Err;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Result::errorPosition() const noexcept
|
|
||||||
{
|
|
||||||
int pos = -1;
|
|
||||||
if (m_result)
|
|
||||||
{
|
|
||||||
const char* p = PQresultErrorField(m_result, PG_DIAG_STATEMENT_POSITION);
|
|
||||||
if (p)
|
|
||||||
pos = std::stoi(std::string(p));
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline sqlpp::postgresql::Result::operator bool() const
|
|
||||||
{
|
|
||||||
return m_result != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Result::clear()
|
|
||||||
{
|
|
||||||
if (m_result)
|
|
||||||
PQclear(m_result);
|
|
||||||
m_result = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Result::affected_rows()
|
|
||||||
{
|
|
||||||
const char* const RowsStr = PQcmdTuples(m_result);
|
|
||||||
return RowsStr[0] ? std::stoi(std::string(RowsStr)) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Result::records_size() const
|
|
||||||
{
|
|
||||||
return m_result ? PQntuples(m_result) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Result::field_count() const
|
|
||||||
{
|
|
||||||
return m_result ? PQnfields(m_result) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Result::isNull(int record, int field) const
|
|
||||||
{
|
|
||||||
/// check index?
|
|
||||||
return PQgetisnull(m_result, record, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Result::length(int record, int field) const
|
|
||||||
{
|
|
||||||
/// check index?
|
|
||||||
return PQgetlength(m_result, record, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Result::~Result()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ExecStatusType Result::status()
|
|
||||||
{
|
|
||||||
return PQresultStatus(m_result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,11 +44,11 @@ namespace sqlpp
|
|||||||
|
|
||||||
inline postgresql::context_t& serialize(const blob_operand& t, postgresql::context_t& context)
|
inline postgresql::context_t& serialize(const blob_operand& t, postgresql::context_t& context)
|
||||||
{
|
{
|
||||||
constexpr char hexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
constexpr char hex_chars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||||
context << "'\\x";
|
context << "'\\x";
|
||||||
for (const auto c : t._t)
|
for (const auto c : t._t)
|
||||||
{
|
{
|
||||||
context << hexChars[c >> 4] << hexChars[c & 0x0F];
|
context << hex_chars[c >> 4] << hex_chars[c & 0x0F];
|
||||||
}
|
}
|
||||||
context << '\'';
|
context << '\'';
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user