1
0
Fork 0
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:
King_DuckZ 2015-12-29 17:32:22 +00:00
parent a91e75829f
commit 390b69e150
7 changed files with 360 additions and 147 deletions

View file

@ -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

View file

@ -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