pgLab/pgsql/Pgsql_Value.h
eelke 0ba632afd1 The getAsArray variation that uses a default value for NULL elements crashed when the
array value itself was NULL.

Choosen to return without error adding zero elements to the list. If you really need strict NULL handling do this
using the other functions possibly checking for null before hand to choose other code path.
2018-09-19 08:45:01 +02:00

179 lines
4 KiB
C++

#ifndef PGSQL_VALUE_H
#define PGSQL_VALUE_H
#include "Pgsql_declare.h"
#include "Pgsql_oids.h"
#include "ArrayParser.h"
#include <sstream>
#include <vector>
#include <QString>
#include <QDateTime>
#include <boost/optional.hpp>
namespace Pgsql {
/** \brief Class that is returned as value of a cell to facilitate auto conversion.
*/
class Value {
public:
const char *empty_str = "";
Value(const char *val, Oid typ);
QString asQString() const;
bool null() const { return m_val == nullptr; }
const char* c_str() const { return m_val == nullptr ? empty_str : m_val; }
operator QString() const;
operator QDateTime() const;
operator QDate() const;
operator QTime() const;
operator std::string() const;
operator int16_t() const;
operator int32_t() const;
operator Oid() const;
operator int64_t() const;
operator bool() const;
operator float() const;
operator double() const;
bool isString() const;
/**
*
* \param insert_iter An insert_iterator which is used to add the elements of the array to a container.
*/
template <typename E, typename I>
void getAsArray(I insert_iter, NullHandling nullhandling = NullHandling::Throw) const
{
if (m_val == nullptr) {
if (nullhandling == NullHandling::Throw)
throw std::runtime_error("Unexpected NULL value for array");
}
else {
using value_type = E;
ArrayParser parser(m_val);
for (;;) {
auto res = parser.GetNextElem();
if (res.ok) {
if (res.value) {
std::string str(res.value->data(), res.value->length());
Value val(str.c_str(), OidFor<E>::elem());
value_type v;
v << val;
insert_iter = v;
}
else {
if (nullhandling == NullHandling::Throw)
throw std::runtime_error("Unexpected NULL value in array");
}
}
else
break;
}
}
}
template <typename E, typename I>
void getAsArray(I insert_iter, const E &value_for_nulls) const
{
if (m_val == nullptr) return;
using value_type = E;
ArrayParser parser(m_val);
for (;;) {
auto res = parser.GetNextElem();
if (res.ok) {
if (res.value) {
std::string str(res.value->data(), res.value->length());
Value val(str.c_str(), OidFor<E>::elem());
value_type v;
v << val;
insert_iter = v;
}
else {
insert_iter = value_for_nulls;
}
}
else
break;
}
}
template <typename E, typename I>
void getAsArrayOfOptional(I insert_iter) const
{
using value_type = E;
ArrayParser parser(m_val);
for (;;) {
auto res = parser.GetNextElem();
if (res.ok) {
if (res.value) {
std::string str(res.value->data(), res.value->length());
Value val(str.c_str(), OidFor<E>::elem());
value_type v;
v << val;
insert_iter = v;
}
else {
insert_iter = std::nullopt;
}
}
else
break;
}
}
/** De catalog uses vector types which are similar to array
* but uses spaces as seperators. AFAIK there are only integral
* vector types so this implementation is only tested for those
*/
template <typename E, typename I>
void getAsVector(I insert_iter) const
{
const char * pos = m_val;
const char * start = pos;
for (;;) {
while (*pos != 0 && *pos != ' ') ++pos;
std::string str(start, pos-start);
Value val(str.c_str(), OidFor<E>::elem());
E v;
v << val;
insert_iter = v;
if (*pos == 0)
break;
start = ++pos; // skip space and set new start to match
}
}
inline Oid getOid() const { return m_typ; }
private:
const char *m_val;
Oid m_typ;
};
template <typename T>
void operator<<(boost::optional<T> &s, const Value &v)
{
if (v.null())
s = boost::optional<T>();
else
*s << v;
}
template <typename T>
void operator<<(std::vector<T> &s, const Value &v)
{
v.getAsArray(std::back_inserter(s));
}
template <typename T>
void operator<<(T &s, const Value &v)
{
s = v.operator T();
}
} // end namespace Pgsql
#endif // PGSQL_VALUE_H