mirror of
https://github.com/KingDuckZ/dindexer.git
synced 2025-08-20 15:40:50 +00:00
Use parametric sql functions to insert new files.
Refactoring so that there are no extra copies of data being inserted.
This commit is contained in:
parent
a91e75829f
commit
390b69e150
7 changed files with 360 additions and 147 deletions
|
@ -24,8 +24,82 @@
|
|||
#include <memory>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include "libpqtypes.h"
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <cassert>
|
||||
|
||||
using sc = std::chrono::system_clock;
|
||||
|
||||
namespace pq {
|
||||
namespace implem {
|
||||
template <> const char* type_to_pqtypes_name<std::string>() { return "%text"; }
|
||||
template <> const char* type_to_pqtypes_name<boost::string_ref>() { return "%text"; }
|
||||
template <> const char* type_to_pqtypes_name<bool>() { return "%bool"; }
|
||||
template <> const char* type_to_pqtypes_name<float>() { return "%float4"; }
|
||||
template <> const char* type_to_pqtypes_name<double>() { return "%float8"; }
|
||||
template <> const char* type_to_pqtypes_name<int16_t>() { return "%int2"; }
|
||||
template <> const char* type_to_pqtypes_name<int32_t>() { return "%int4"; }
|
||||
template <> const char* type_to_pqtypes_name<int64_t>() { return "%int8"; }
|
||||
template <> const char* type_to_pqtypes_name<uint16_t>() { return "%int2"; }
|
||||
template <> const char* type_to_pqtypes_name<uint32_t>() { return "%int4"; }
|
||||
template <> const char* type_to_pqtypes_name<uint64_t>() { return "%int8"; }
|
||||
template <> const char* type_to_pqtypes_name<sc::time_point>() { return "%timestamptz"; }
|
||||
|
||||
template const char* type_to_pqtypes_name<std::string> ( void );
|
||||
template const char* type_to_pqtypes_name<boost::string_ref> ( void );
|
||||
template const char* type_to_pqtypes_name<bool> ( void );
|
||||
template const char* type_to_pqtypes_name<float> ( void );
|
||||
template const char* type_to_pqtypes_name<double> ( void );
|
||||
template const char* type_to_pqtypes_name<int16_t> ( void );
|
||||
template const char* type_to_pqtypes_name<int32_t> ( void );
|
||||
template const char* type_to_pqtypes_name<int64_t> ( void );
|
||||
template const char* type_to_pqtypes_name<uint16_t> ( void );
|
||||
template const char* type_to_pqtypes_name<uint32_t> ( void );
|
||||
template const char* type_to_pqtypes_name<uint64_t> ( void );
|
||||
|
||||
auto get_pqlib_c_type_struct<std::chrono::system_clock::time_point>::conv (const std::chrono::system_clock::time_point& parParam) -> type {
|
||||
static_assert(sizeof(storage) == sizeof(PGtimestamp), "Wrong size for timestamp, please update DATA_SIZE");
|
||||
static_assert(alignof(storage) == alignof(PGtimestamp), "Wrong alignment for timestamp, please update type");
|
||||
|
||||
using std::chrono::system_clock;
|
||||
|
||||
PGtimestamp ts;
|
||||
|
||||
std::memset(&ts, 0, sizeof(PGtimestamp));
|
||||
|
||||
auto t = system_clock::to_time_t(parParam);
|
||||
ts.epoch = t;
|
||||
auto tm = std::localtime(&t);
|
||||
ts.time.hour = tm->tm_hour;
|
||||
ts.time.min = tm->tm_min;
|
||||
ts.time.sec = tm->tm_sec;
|
||||
ts.time.usec = 0;
|
||||
ts.time.withtz = 1;
|
||||
ts.date.isbc = 0;
|
||||
ts.date.year = tm->tm_year + 1900;
|
||||
ts.date.mon = tm->tm_mon;
|
||||
ts.date.mday = tm->tm_mday;
|
||||
char* tzn;
|
||||
PQlocalTZInfo(&t, &ts.time.gmtoff, &ts.time.isdst, &tzn);
|
||||
std::strcpy(ts.time.tzabbr, tzn);
|
||||
|
||||
std::copy(reinterpret_cast<const char*>(&ts), reinterpret_cast<const char*>(&ts) + sizeof(ts), reinterpret_cast<char*>(&m_storage));
|
||||
return &m_storage;
|
||||
}
|
||||
|
||||
get_pqlib_c_type_struct<std::chrono::system_clock::time_point>::~get_pqlib_c_type_struct ( void ) noexcept {
|
||||
return;
|
||||
}
|
||||
} //namespace implem
|
||||
|
||||
namespace {
|
||||
int call_PQputf (PGparam* parParam, const std::string* parTypes, va_list parArgp) {
|
||||
return PQputvf(parParam, nullptr, 0, parTypes->c_str(), parArgp);
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
struct Connection::LocalData {
|
||||
PGconn* connection;
|
||||
};
|
||||
|
@ -81,10 +155,13 @@ namespace pq {
|
|||
throw DatabaseException(oss.str(), std::move(err), __FILE__, __LINE__);
|
||||
}
|
||||
query_void("SET NAMES 'utf8'");
|
||||
|
||||
PQinitTypes(m_localData->connection); //Init libpqtypes
|
||||
}
|
||||
|
||||
void Connection::disconnect() {
|
||||
if (is_connected()) {
|
||||
PQclearTypes(m_localData->connection); //clear libpqtypes
|
||||
PQfinish(m_localData->connection);
|
||||
m_localData->connection = nullptr;
|
||||
}
|
||||
|
@ -134,4 +211,41 @@ namespace pq {
|
|||
PQArrayType clean_str(PQescapeLiteral(m_localData->connection, parString.data(), parString.size()), &PQfreemem);
|
||||
return std::string(clean_str.get());
|
||||
}
|
||||
|
||||
void Connection::query_void_params (const std::string& parQuery, PGParams& parParams) {
|
||||
auto deleter = [](PGresult* r) { PQclear(r); };
|
||||
using ResultPtr = std::unique_ptr<PGresult, decltype(deleter)>;
|
||||
|
||||
int result_format = 1;
|
||||
assert(parParams.get());
|
||||
auto res = ResultPtr(
|
||||
PQparamExec(
|
||||
m_localData->connection,
|
||||
parParams.get(),
|
||||
parQuery.c_str(),
|
||||
result_format
|
||||
),
|
||||
deleter
|
||||
);
|
||||
if (not res) {
|
||||
std::ostringstream oss;
|
||||
oss << "Error allocating result object while running \"" << parQuery << "\": " << PQgeterror();
|
||||
throw DatabaseException("Error running query", oss.str(), __FILE__, __LINE__);
|
||||
}
|
||||
const int ress = PQresultStatus(res.get());
|
||||
if (ress != PGRES_TUPLES_OK && ress != PGRES_COMMAND_OK) {
|
||||
throw DatabaseException("Error running query", error_message(), __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
auto Connection::make_params (const std::string* parTypes, ...) -> PGParams {
|
||||
PGParams retval(PQparamCreate(m_localData->connection), &PQparamClear);
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, parTypes);
|
||||
call_PQputf(retval.get(), parTypes, argp);
|
||||
va_end(argp);
|
||||
|
||||
return std::move(retval);
|
||||
}
|
||||
} //namespace pq
|
||||
|
|
|
@ -23,6 +23,11 @@
|
|||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
|
||||
struct pg_param;
|
||||
typedef pg_param PGparam;
|
||||
|
||||
namespace pq {
|
||||
class Connection {
|
||||
|
@ -40,8 +45,15 @@ namespace pq {
|
|||
std::string escaped_literal ( const std::string& parString );
|
||||
std::string escaped_literal ( boost::string_ref parString );
|
||||
|
||||
template <typename... Args>
|
||||
void query_void ( const std::string& parQuery, Args&&... parArgs );
|
||||
|
||||
private:
|
||||
struct LocalData;
|
||||
using PGParams = std::unique_ptr<::PGparam, void(*)(::PGparam*)>;
|
||||
|
||||
void query_void_params ( const std::string& parQuery, PGParams& parParams );
|
||||
PGParams make_params ( const std::string* parTypes, ... );
|
||||
|
||||
const std::string m_username;
|
||||
const std::string m_passwd;
|
||||
|
@ -51,6 +63,71 @@ namespace pq {
|
|||
std::unique_ptr<LocalData> m_localData;
|
||||
};
|
||||
|
||||
namespace implem {
|
||||
template <typename T>
|
||||
const char* type_to_pqtypes_name ( void );
|
||||
|
||||
template <typename T>
|
||||
struct get_pqlib_c_type_struct {
|
||||
using type = T;
|
||||
static type conv ( T parParam ) { return parParam; }
|
||||
};
|
||||
template <>
|
||||
struct get_pqlib_c_type_struct<std::string> {
|
||||
using type = const char*;
|
||||
static type conv ( const std::string& parParam ) { return parParam.c_str(); }
|
||||
};
|
||||
template <>
|
||||
struct get_pqlib_c_type_struct<boost::string_ref> {
|
||||
using type = const char*;
|
||||
static type conv ( const boost::string_ref& parParam ) { return parParam.data(); }
|
||||
};
|
||||
template <>
|
||||
struct get_pqlib_c_type_struct<bool> {
|
||||
using type = int;
|
||||
static type conv ( bool parParam ) { return (parParam ? 1 : 0); }
|
||||
};
|
||||
template <>
|
||||
struct get_pqlib_c_type_struct<std::chrono::system_clock::time_point> {
|
||||
struct StorageStruct { uint64_t epoch; int a[14]; char tzabbr[16]; };
|
||||
static constexpr std::size_t DATA_SIZE = sizeof(StorageStruct);
|
||||
using storage = std::aligned_storage<DATA_SIZE, alignof(uint64_t)>::type;
|
||||
storage m_storage;
|
||||
|
||||
public:
|
||||
using type = const storage*;
|
||||
|
||||
type conv ( const std::chrono::system_clock::time_point& parParam );
|
||||
~get_pqlib_c_type_struct ( void ) noexcept;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline typename get_pqlib_c_type_struct<T>::type get_pqlib_c_type (const T& parParam) {
|
||||
return get_pqlib_c_type_struct<T>::conv(parParam);
|
||||
}
|
||||
} //namespace implem
|
||||
|
||||
template <typename... Args>
|
||||
void Connection::query_void (const std::string& parQuery, Args&&... parArgs) {
|
||||
using std::remove_cv;
|
||||
using std::remove_reference;
|
||||
|
||||
auto make_pgparams = [&parArgs..., this](){
|
||||
using implem::type_to_pqtypes_name;
|
||||
|
||||
std::string types;
|
||||
int unpack[] {0, (types += type_to_pqtypes_name<typename remove_cv<typename remove_reference<Args>::type>::type>(), types += ' ', 0)...};
|
||||
if (not types.empty()) {
|
||||
types.resize(types.size() - 1);
|
||||
}
|
||||
static_cast<void>(unpack);
|
||||
|
||||
return this->make_params(&types, implem::get_pqlib_c_type_struct<typename remove_cv<typename remove_reference<Args>::type>::type>().conv(parArgs)...);
|
||||
};
|
||||
PGParams pgparams = make_pgparams();
|
||||
|
||||
this->query_void_params(parQuery, pgparams);
|
||||
}
|
||||
} //namespace pq
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue