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,66 +142,7 @@ 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())
{
// 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))
{
_handle->count++;
}
else
{
return false;
}
}
// Really needed?
if (_handle->fields == 0U)
{
_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)
void _bind_boolean_result(size_t _index, signed char* value, bool* is_null)
{
auto index = static_cast<int>(_index);
if (_handle->debug())
@ -165,11 +150,11 @@ namespace sqlpp
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);
*is_null = _handle->result.is_null(_handle->count, index);
*value = _handle->result.get_bool_value(_handle->count, index);
}
inline void bind_result_t::_bind_floating_point_result(size_t _index, double* value, bool* is_null)
void _bind_floating_point_result(size_t _index, double* value, bool* is_null)
{
auto index = static_cast<int>(_index);
if (_handle->debug())
@ -177,11 +162,11 @@ namespace sqlpp
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);
*is_null = _handle->result.is_null(_handle->count, index);
*value = _handle->result.get_double_value(_handle->count, index);
}
inline void bind_result_t::_bind_integral_result(size_t _index, int64_t* value, bool* is_null)
void _bind_integral_result(size_t _index, int64_t* value, bool* is_null)
{
auto index = static_cast<int>(_index);
if (_handle->debug())
@ -189,11 +174,11 @@ namespace sqlpp
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);
*is_null = _handle->result.is_null(_handle->count, index);
*value = _handle->result.get_int64_value(_handle->count, index);
}
inline void bind_result_t::_bind_unsigned_integral_result(size_t _index, uint64_t* value, bool* is_null)
void _bind_unsigned_integral_result(size_t _index, uint64_t* value, bool* is_null)
{
auto index = static_cast<int>(_index);
if (_handle->debug())
@ -201,11 +186,11 @@ namespace sqlpp
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);
*is_null = _handle->result.is_null(_handle->count, index);
*value = _handle->result.get_uint64_value(_handle->count, index);
}
inline void bind_result_t::_bind_text_result(size_t _index, const char** value, size_t* len)
void _bind_text_result(size_t _index, const char** value, size_t* len)
{
auto index = static_cast<int>(_index);
if (_handle->debug())
@ -213,14 +198,14 @@ namespace sqlpp
std::cerr << "PostgreSQL debug: binding text result at index: " << index << std::endl;
}
if (_handle->result.isNull(_handle->count, index))
if (_handle->result.is_null(_handle->count, index))
{
*value = nullptr;
*len = 0;
}
else
{
*value = _handle->result.getCharPtrValue(_handle->count, index);
*value = _handle->result.get_char_ptr_value(_handle->count, index);
*len = static_cast<size_t>(_handle->result.length(_handle->count, index));
}
}
@ -233,8 +218,7 @@ namespace sqlpp
// 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)
void _bind_date_result(size_t _index, ::sqlpp::chrono::day_point* value, bool* is_null)
{
auto index = static_cast<int>(_index);
@ -243,11 +227,11 @@ namespace sqlpp
std::cerr << "PostgreSQL debug: binding date result at index: " << index << std::endl;
}
*is_null = _handle->result.isNull(_handle->count, index);
*is_null = _handle->result.is_null(_handle->count, index);
if (!(*is_null))
{
const auto date_string = _handle->result.getCharPtrValue(_handle->count, index);
const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index);
if (_handle->debug())
{
@ -277,7 +261,7 @@ namespace sqlpp
}
// 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)
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())
@ -285,11 +269,11 @@ namespace sqlpp
std::cerr << "PostgreSQL debug: binding date_time result at index: " << index << std::endl;
}
*is_null = _handle->result.isNull(_handle->count, index);
*is_null = _handle->result.is_null(_handle->count, index);
if (!(*is_null))
{
const auto date_string = _handle->result.getCharPtrValue(_handle->count, index);
const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index);
if (_handle->debug())
{
@ -333,7 +317,7 @@ namespace sqlpp
}
// 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)
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())
@ -341,11 +325,11 @@ namespace sqlpp
std::cerr << "PostgreSQL debug: binding time result at index: " << index << std::endl;
}
*is_null = _handle->result.isNull(_handle->count, index);
*is_null = _handle->result.is_null(_handle->count, index);
if (!(*is_null))
{
const auto time_string = _handle->result.getCharPtrValue(_handle->count, index);
const auto time_string = _handle->result.get_char_ptr_value(_handle->count, index);
if (_handle->debug())
{
@ -382,30 +366,30 @@ namespace sqlpp
}
}
inline void bind_result_t::_bind_blob_result(size_t _index, const uint8_t** value, size_t* len)
void _bind_blob_result(size_t _index, const uint8_t** value, size_t* len)
{
auto index = static_cast<int>(_index);
if (_handle->debug())
{
std::cerr << "PostgreSQL debug: binding blob result at index: " << index << std::endl;
}
if (_handle->result.isNull(_handle->count, index))
if (_handle->result.is_null(_handle->count, index))
{
*value = nullptr;
*len = 0;
}
else
{
*value = _handle->result.getBlobValue(_handle->count, index);
*value = _handle->result.get_blob_value(_handle->count, index);
*len = static_cast<size_t>(_handle->result.length(_handle->count, index));
}
}
inline int bind_result_t::size() const
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,127 +421,7 @@ namespace sqlpp
}
//! set the default transaction isolation level to use for new transactions
void set_default_isolation_level(isolation_level level);
//! get the currently set default transaction isolation level
isolation_level get_default_isolation_level();
//! create savepoint
void savepoint(const std::string& name);
//! ROLLBACK TO SAVEPOINT
void rollback_to_savepoint(const std::string& name);
//! release_savepoint
void release_savepoint(const std::string& name);
//! start transaction
void start_transaction(isolation_level level = isolation_level::undefined);
//! commit transaction (or throw transaction if transaction has
// finished already)
void commit_transaction();
//! rollback transaction
void rollback_transaction(bool report);
//! report rollback failure
void report_rollback_failure(const std::string& message) noexcept;
//! get the last inserted id for a certain table
uint64_t last_insert_id(const std::string& table, const std::string& fieldname);
::PGconn* native_handle() const;
protected:
_handle_ptr_t _handle;
// Constructors
connection_base() = default;
connection_base(_handle_ptr_t&& handle) : _handle{std::move(handle)}
{
}
};
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)
void set_default_isolation_level(isolation_level level)
{
std::string level_str = "read uncommmitted";
switch (level)
@ -494,7 +447,7 @@ namespace sqlpp
}
//! get the currently set default transaction isolation level
inline isolation_level connection_base::get_default_isolation_level()
isolation_level get_default_isolation_level()
{
auto res = execute("SHOW default_transaction_isolation;");
auto status = res->result.status();
@ -503,7 +456,7 @@ namespace sqlpp
throw sqlpp::exception("PostgreSQL error: could not read default_transaction_isolation");
}
auto in = res->result.getStringValue(0, 0);
auto in = res->result.get_string_value(0, 0);
if (in == "read committed")
{
return isolation_level::read_committed;
@ -523,22 +476,29 @@ namespace sqlpp
return isolation_level::undefined;
}
// TODO: Fix escaping.
inline std::string connection_base::escape(const std::string& s) const
//! create savepoint
void savepoint(const std::string& name)
{
validate_connection_handle();
// Escape strings
std::string result;
result.resize((s.size() * 2) + 1);
/// NOTE prevent from sql injection?
execute("SAVEPOINT " + name);
}
int err;
size_t length = PQescapeStringConn(native_handle(), &result[0], s.c_str(), s.size(), &err);
result.resize(length);
return result;
//! ROLLBACK TO SAVEPOINT
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)
{
/// NOTE prevent from sql injection?
execute("RELEASE SAVEPOINT " + name);
}
//! start transaction
inline void connection_base::start_transaction(isolation_level level)
void start_transaction(isolation_level level = isolation_level::undefined)
{
if (_transaction_active)
{
@ -575,29 +535,8 @@ namespace sqlpp
_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()
void commit_transaction()
{
if (!_transaction_active)
{
@ -609,7 +548,7 @@ namespace sqlpp
}
//! rollback transaction
inline void connection_base::rollback_transaction(bool report)
void rollback_transaction(bool report)
{
if (!_transaction_active)
{
@ -625,12 +564,13 @@ namespace sqlpp
}
//! report rollback failure
inline void connection_base::report_rollback_failure(const std::string& message) noexcept
void 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)
//! get the last inserted id for a certain table
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());
@ -647,11 +587,22 @@ namespace sqlpp
return std::stoul(in);
}
inline ::PGconn* connection_base::native_handle() const
::PGconn* native_handle() const
{
return _handle->native_handle();
}
protected:
_handle_ptr_t _handle;
// Constructors
connection_base() = default;
connection_base(_handle_ptr_t&& handle) : _handle{std::move(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,67 +102,23 @@ 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(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();
void execute();
std::string name() const
{
return _name;
}
private:
void generate_name();
void prepare(std::string stmt);
};
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)
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));
}
inline prepared_statement_handle_t::~prepared_statement_handle_t()
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()
{
if (valid && !_name.empty())
{
@ -150,25 +126,31 @@ namespace sqlpp
}
}
inline void prepared_statement_handle_t::execute()
void execute()
{
const size_t size = paramValues.size();
const size_t size = param_values.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()));
values.push_back(null_values[i] ? nullptr : const_cast<char*>(param_values[i].c_str()));
// Execute prepared statement with the parameters.
clearResult();
clear_result();
valid = false;
count = 0;
totalCount = 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;
}
inline void prepared_statement_handle_t::generate_name()
std::string name() const
{
return _name;
}
private:
void generate_name()
{
// Generate a random name for the prepared statement
while (connection.prepared_statement_names.find(_name) != connection.prepared_statement_names.end())
@ -184,12 +166,13 @@ namespace sqlpp
connection.prepared_statement_names.insert(_name);
}
inline void prepared_statement_handle_t::prepare(std::string stmt)
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;
}
};
}
}
}

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,28 +83,7 @@ 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);
};
// 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)
void _bind_boolean_parameter(size_t index, const signed char* value, bool is_null)
{
if (_handle->debug())
{
@ -101,21 +91,21 @@ namespace sqlpp
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
}
_handle->nullValues[index] = is_null;
_handle->null_values[index] = is_null;
if (!is_null)
{
if (*value)
{
_handle->paramValues[index] = "TRUE";
_handle->param_values[index] = "TRUE";
}
else
{
_handle->paramValues[index] = "FALSE";
_handle->param_values[index] = "FALSE";
}
}
}
inline void prepared_statement_t::_bind_floating_point_parameter(size_t index, const double* value, bool is_null)
void _bind_floating_point_parameter(size_t index, const double* value, bool is_null)
{
if (_handle->debug())
{
@ -123,16 +113,16 @@ namespace sqlpp
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
}
_handle->nullValues[index] = is_null;
_handle->null_values[index] = is_null;
if (!is_null)
{
sqlpp::detail::float_safe_ostringstream out;
out << *value;
_handle->paramValues[index] = out.str();
_handle->param_values[index] = out.str();
}
}
inline void prepared_statement_t::_bind_integral_parameter(size_t index, const int64_t* value, bool is_null)
void _bind_integral_parameter(size_t index, const int64_t* value, bool is_null)
{
if (_handle->debug())
{
@ -141,14 +131,14 @@ namespace sqlpp
}
// Assign values
_handle->nullValues[index] = is_null;
_handle->null_values[index] = is_null;
if (!is_null)
{
_handle->paramValues[index] = std::to_string(*value);
_handle->param_values[index] = std::to_string(*value);
}
}
inline void prepared_statement_t::_bind_text_parameter(size_t index, const std::string* value, bool is_null)
void _bind_text_parameter(size_t index, const std::string* value, bool is_null)
{
if (_handle->debug())
{
@ -157,43 +147,43 @@ namespace sqlpp
}
// Assign values
_handle->nullValues[index] = is_null;
_handle->null_values[index] = is_null;
if (!is_null)
{
_handle->paramValues[index] = *value;
_handle->param_values[index] = *value;
}
}
inline void prepared_statement_t::_bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null)
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->nullValues[index] = is_null;
_handle->null_values[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();
_handle->param_values[index] = os.str();
if (_handle->debug())
{
std::cerr << "PostgreSQL debug: binding date parameter string: " << _handle->paramValues[index] << std::endl;
std::cerr << "PostgreSQL debug: binding date parameter string: " << _handle->param_values[index] << std::endl;
}
}
}
inline void prepared_statement_t::_bind_time_of_day_parameter(size_t index, const ::std::chrono::microseconds* value, bool is_null)
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->nullValues[index] = is_null;
_handle->null_values[index] = is_null;
if (not is_null)
{
const auto time = ::date::make_time(*value) ;
@ -201,22 +191,22 @@ namespace sqlpp
// Timezone handling - always treat the local value as UTC.
std::ostringstream os;
os << time << "+00";
_handle->paramValues[index] = os.str();
_handle->param_values[index] = os.str();
if (_handle->debug())
{
std::cerr << "PostgreSQL debug: binding time parameter string: " << _handle->paramValues[index] << std::endl;
std::cerr << "PostgreSQL debug: binding time parameter string: " << _handle->param_values[index] << std::endl;
}
}
}
inline void prepared_statement_t::_bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null)
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->nullValues[index] = is_null;
_handle->null_values[index] = is_null;
if (not is_null)
{
const auto dp = ::sqlpp::chrono::floor<::date::days>(*value);
@ -226,40 +216,41 @@ namespace sqlpp
// Timezone handling - always treat the local value as UTC.
std::ostringstream os;
os << ymd << ' ' << time << "+00";
_handle->paramValues[index] = os.str();
_handle->param_values[index] = os.str();
if (_handle->debug())
{
std::cerr << "PostgreSQL debug: binding date_time parameter string: " << _handle->paramValues[index] << std::endl;
std::cerr << "PostgreSQL debug: binding date_time parameter string: " << _handle->param_values[index] << std::endl;
}
}
}
inline void prepared_statement_t::_bind_blob_parameter(size_t index, const std::vector<unsigned char>* value, bool is_null)
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->nullValues[index] = is_null;
_handle->null_values[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'};
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] = hexChars[c >> 4];
param[++i] = hexChars[c & 0x0F];
param[++i] = hex_chars[c >> 4];
param[++i] = hex_chars[c & 0x0F];
}
_handle->paramValues[index] = std::move(param);
_handle->param_values[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;
std::cerr << "PostgreSQL debug: binding blob parameter string (up to 100 chars): " << _handle->param_values[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,51 +189,14 @@ 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);
// 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;
PGresult* m_result;
std::string m_query;
};
inline Result::Result() : m_result(nullptr)
void check_status() const
{
const std::string err = status_error();
if (!err.empty())
throw_sql_error(err, query());
}
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
[[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);
@ -199,39 +207,39 @@ namespace sqlpp
switch (code[1])
{
case '8':
throw broken_connection(Err);
throw broken_connection(err);
case 'A':
throw feature_not_supported(Err, Query);
throw feature_not_supported(err, query);
}
break;
case '2':
switch (code[1])
{
case '2':
throw data_exception(Err, Query);
throw data_exception(err, query);
case '3':
if (strcmp(code, "23001") == 0)
throw restrict_violation(Err, Query);
throw restrict_violation(err, query);
if (strcmp(code, "23502") == 0)
throw not_null_violation(Err, Query);
throw not_null_violation(err, query);
if (strcmp(code, "23503") == 0)
throw foreign_key_violation(Err, Query);
throw foreign_key_violation(err, query);
if (strcmp(code, "23505") == 0)
throw unique_violation(Err, Query);
throw unique_violation(err, query);
if (strcmp(code, "23514") == 0)
throw check_violation(Err, Query);
throw integrity_constraint_violation(Err, Query);
throw check_violation(err, query);
throw integrity_constraint_violation(err, query);
case '4':
throw invalid_cursor_state(Err, Query);
throw invalid_cursor_state(err, query);
case '6':
throw invalid_sql_statement_name(Err, Query);
throw invalid_sql_statement_name(err, query);
}
break;
case '3':
switch (code[1])
{
case '4':
throw invalid_cursor_name(Err, Query);
throw invalid_cursor_name(err, query);
}
break;
case '4':
@ -239,15 +247,15 @@ namespace sqlpp
{
case '2':
if (strcmp(code, "42501") == 0)
throw insufficient_privilege(Err, Query);
throw insufficient_privilege(err, query);
if (strcmp(code, "42601") == 0)
throw syntax_error(Err, Query, errorPosition());
throw syntax_error(err, query, error_position());
if (strcmp(code, "42703") == 0)
throw undefined_column(Err, Query);
throw undefined_column(err, query);
if (strcmp(code, "42883") == 0)
throw undefined_function(Err, Query);
throw undefined_function(err, query);
if (strcmp(code, "42P01") == 0)
throw undefined_table(Err, Query);
throw undefined_table(err, query);
}
break;
case '5':
@ -255,36 +263,36 @@ namespace sqlpp
{
case '3':
if (strcmp(code, "53100") == 0)
throw disk_full(Err, Query);
throw disk_full(err, query);
if (strcmp(code, "53200") == 0)
throw out_of_memory(Err, Query);
throw out_of_memory(err, query);
if (strcmp(code, "53300") == 0)
throw too_many_connections(Err);
throw insufficient_resources(Err, Query);
throw too_many_connections(err);
throw insufficient_resources(err, query);
}
break;
case 'P':
if (strcmp(code, "P0001") == 0)
throw plpgsql_raise(Err, Query);
throw plpgsql_raise(err, query);
if (strcmp(code, "P0002") == 0)
throw plpgsql_no_data_found(Err, Query);
throw plpgsql_no_data_found(err, query);
if (strcmp(code, "P0003") == 0)
throw plpgsql_too_many_rows(Err, Query);
throw plpgsql_error(Err, Query);
throw plpgsql_too_many_rows(err, query);
throw plpgsql_error(err, query);
break;
default:
throw sql_user_error(Err, Query, code);
throw sql_user_error(err, query, code);
}
throw sql_error(Err, Query);
throw sql_error(err, query);
}
inline std::string Result::StatusError() const
std::string status_error() const
{
if (!m_result)
throw failure("No result set given");
std::string Err;
std::string err;
switch (PQresultStatus(m_result))
{
@ -300,7 +308,7 @@ namespace sqlpp
case PGRES_BAD_RESPONSE: // The server's response was not understood
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
Err = PQresultErrorMessage(m_result);
err = PQresultErrorMessage(m_result);
break;
#if PG_MAJORVERSION_NUM >= 13
case PGRES_COPY_BOTH:
@ -314,10 +322,10 @@ namespace sqlpp
throw sqlpp::exception("pqxx::result: Unrecognized response code " +
std::to_string(PQresultStatus(m_result)));
}
return Err;
return err;
}
inline int Result::errorPosition() const noexcept
int error_position() const noexcept
{
int pos = -1;
if (m_result)
@ -329,54 +337,21 @@ namespace sqlpp
return pos;
}
inline sqlpp::postgresql::Result::operator bool() const
void check_index(int record, int field) const noexcept(false)
{
return m_result != 0;
if (record > records_size() || field > field_count())
throw std::out_of_range("PostgreSQL error: index out of range");
}
inline void Result::clear()
// move PQgetvalue to implementation so we don't depend on the libpq in the
// public interface
const char* get_pq_value(PGresult* result, int record, int field) const
{
if (m_result)
PQclear(m_result);
m_result = nullptr;
return const_cast<const char*>(PQgetvalue(result, record, field));
}
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);
}
PGresult* m_result;
std::string m_query;
};
}
}

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 << '\'';