0
0
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:
MeanSquaredError 2023-08-07 20:37:59 +03:00 committed by GitHub
parent c03ac660ee
commit d800f4d6fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1000 additions and 1121 deletions

View File

@ -56,11 +56,55 @@ namespace sqlpp
private:
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:
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(bind_result_t&&) = default;
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_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())
void _bind_boolean_result(size_t _index, signed char* value, bool* is_null)
{
// cerr
std::cerr << "PostgreSQL debug: constructing bind result, using handle at: " << this->_handle.get()
<< 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))
auto index = static_cast<int>(_index);
if (_handle->debug())
{
_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
{
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?
if (_handle->fields == 0U)
// 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 !
void _bind_date_result(size_t _index, ::sqlpp::chrono::day_point* value, bool* is_null)
{
_handle->fields = _handle->result.field_count();
}
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);
auto index = static_cast<int>(_index);
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})"};
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
};
} else {
if (_handle->debug()) {
std::cerr << "PostgreSQL debug: got invalid date '" << date_string << "'" << std::endl;
*is_null = _handle->result.is_null(_handle->count, index);
if (!(*is_null))
{
const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index);
if (_handle->debug())
{
std::cerr << "PostgreSQL debug: date string: " << date_string << std::endl;
}
*value = {};
}
}
else
{
*value = {};
}
}
// always returns UTC time for timestamp with time zone
inline void bind_result_t::_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 date_time 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())
{
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;
static const std::regex rx {"(\\d{4})-(\\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
};
} else {
if (_handle->debug()) {
std::cerr << "PostgreSQL debug: got invalid date '" << date_string << "'" << std::endl;
}
} else {
if (_handle->debug()) {
std::cerr << "PostgreSQL debug: got invalid date_time '" << date_string << "'" << std::endl;
*value = {};
}
}
else
{
*value = {};
}
}
}
// always returns UTC time for time with time zone
inline void bind_result_t::_bind_time_of_day_result(size_t _index, ::std::chrono::microseconds* value, bool* is_null)
{
auto index = static_cast<int>(_index);
if (_handle->debug())
// always returns UTC time for timestamp with time zone
void _bind_date_time_result(size_t _index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
{
std::cerr << "PostgreSQL debug: binding time result at index: " << index << std::endl;
}
*is_null = _handle->result.isNull(_handle->count, index);
if (!(*is_null))
{
const auto time_string = _handle->result.getCharPtrValue(_handle->count, index);
auto index = static_cast<int>(_index);
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 {
"(\\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;
*is_null = _handle->result.is_null(_handle->count, index);
if (!(*is_null))
{
const auto date_string = _handle->result.get_char_ptr_value(_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 {
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)
{
auto index = static_cast<int>(_index);
if (_handle->debug())
// 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)
{
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;
*len = 0;
}
else
{
*value = _handle->result.getBlobValue(_handle->count, index);
*len = static_cast<size_t>(_handle->result.length(_handle->count, index));
}
}
auto index = static_cast<int>(_index);
if (_handle->debug())
{
std::cerr << "PostgreSQL debug: binding blob result at index: " << index << std::endl;
}
inline int bind_result_t::size() const
{
return _handle->result.records_size();
}
if (_handle->result.is_null(_handle->count, index))
{
*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 sqlpp

View File

@ -62,10 +62,9 @@ namespace sqlpp
namespace detail
{
// Forward declaration
inline std::unique_ptr<detail::prepared_statement_handle_t> prepare_statement(std::unique_ptr<connection_handle>& handle,
const std::string& stmt,
const size_t& paramCount)
const size_t& param_count)
{
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
(*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)
@ -144,18 +143,67 @@ namespace sqlpp
}
// direct execution
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);
size_t remove_impl(const std::string& stmt);
bind_result_t select_impl(const std::string& stmt)
{
return execute(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_statement_t prepare_impl(const std::string& stmt, const size_t& paramCount);
bind_result_t run_prepared_select_impl(prepared_statement_t& prep);
size_t run_prepared_execute_impl(prepared_statement_t& prep);
size_t run_prepared_insert_impl(prepared_statement_t& prep);
size_t run_prepared_update_impl(prepared_statement_t& prep);
size_t run_prepared_remove_impl(prepared_statement_t& prep);
prepared_statement_t prepare_impl(const std::string& stmt, const size_t& param_count)
{
validate_connection_handle();
return {prepare_statement(_handle, stmt, param_count)};
}
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:
using _connection_base_t = connection_base;
@ -284,7 +332,20 @@ namespace sqlpp
}
// 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 <
typename Execute,
@ -313,7 +374,19 @@ namespace sqlpp
}
// 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
template <typename T>
@ -348,37 +421,176 @@ namespace sqlpp
}
//! 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
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
void savepoint(const std::string& name);
void savepoint(const std::string& name)
{
/// NOTE prevent from sql injection?
execute("SAVEPOINT " + name);
}
//! 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
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
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
// finished already)
void commit_transaction();
//! commit transaction (or throw transaction if transaction has finished already)
void commit_transaction()
{
if (!_transaction_active)
{
throw sqlpp::exception("PostgreSQL error: transaction failed or finished.");
}
_transaction_active = false;
execute("COMMIT");
}
//! 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
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
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:
_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)
{
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();
}
// Method definition moved outside of class because it needs connection_base
inline std::string context_t::escape(const std::string& arg) const
{
return _db.escape(arg);

View File

@ -52,11 +52,6 @@ namespace sqlpp
namespace detail
{
inline void handle_cleanup(PGconn* postgres)
{
PQfinish(postgres);
}
struct DLL_LOCAL connection_handle
{
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)
: config(conf), postgres{nullptr, handle_cleanup}
: config(conf), postgres{nullptr, PQfinish}
{
#ifdef SQLPP_DYNAMIC_LOADING
init_pg("");

View File

@ -54,25 +54,45 @@ namespace sqlpp
{
struct DLL_PUBLIC statement_handle_t
{
detail::connection_handle& connection;
connection_handle& connection;
Result result;
bool valid = false;
int count = 0;
int totalCount = 0;
int total_count = 0;
int fields = 0;
// 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(statement_handle_t&&) = delete;
statement_handle_t& operator=(const statement_handle_t&) = delete;
statement_handle_t& operator=(statement_handle_t&&) = delete;
virtual ~statement_handle_t();
bool operator!() const;
void clearResult();
virtual ~statement_handle_t()
{
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
@ -82,19 +102,47 @@ namespace sqlpp
public:
// Store prepared statement arguments
std::vector<bool> nullValues;
std::vector<std::string> paramValues;
std::vector<bool> null_values;
std::vector<std::string> param_values;
// 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(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;
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
{
@ -102,94 +150,29 @@ namespace sqlpp
}
private:
void generate_name();
void prepare(std::string stmt);
void 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);
}
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;
}
}
}
}

View File

@ -60,7 +60,18 @@ namespace sqlpp
public:
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(prepared_statement_t&&) = default;
prepared_statement_t& operator=(const prepared_statement_t&) = delete;
@ -72,194 +83,174 @@ namespace sqlpp
return (this->_handle == rhs._handle);
}
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);
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);
void _bind_time_of_day_parameter(size_t index, const ::std::chrono::microseconds* value, bool is_null);
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);
void _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->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;
}
}
}
}
}

View File

@ -53,26 +53,71 @@ namespace sqlpp
class DLL_PUBLIC Result
{
public:
Result();
~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
Result() : m_result(nullptr)
{
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{};
const auto txt = std::string(getPqValue(m_result, record, field));
const auto txt = std::string(get_pq_value(m_result, record, field));
if(txt != "")
{
t = std::stoll(txt);
@ -81,11 +126,11 @@ namespace sqlpp
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{};
const auto txt = std::string(getPqValue(m_result, record, field));
const auto txt = std::string(get_pq_value(m_result, record, field));
if(txt != "")
{
t = std::stoull(txt);
@ -94,11 +139,11 @@ namespace sqlpp
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 txt = std::string(getPqValue(m_result, record, field));
auto txt = std::string(get_pq_value(m_result, record, field));
if(txt != "")
{
t = std::stod(txt);
@ -107,25 +152,25 @@ namespace sqlpp
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);
auto val = getPqValue(m_result, record, field);
check_index(record, field);
auto val = get_pq_value(m_result, record, field);
if (*val == 't')
return true;
else if (*val == 'f')
@ -144,239 +189,169 @@ namespace sqlpp
}
private:
void CheckStatus() const;
[[noreturn]] void ThrowSQLError(const std::string& Err, const std::string& Query) const;
std::string StatusError() const;
int errorPosition() const noexcept;
bool hasError();
void checkIndex(int record, int field) const noexcept(false);
void check_status() const
{
const std::string err = status_error();
if (!err.empty())
throw_sql_error(err, query());
}
[[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
// 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;
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);
}
}
}

View File

@ -44,11 +44,11 @@ namespace sqlpp
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";
for (const auto c : t._t)
{
context << hexChars[c >> 4] << hexChars[c & 0x0F];
context << hex_chars[c >> 4] << hex_chars[c & 0x0F];
}
context << '\'';