to the array element type. A five line construct had been copy pasted to achieve conversion by using the capabilities of the Value object however this was not going to work for types that are not known to the database as adding support for these to Value would be a bad idea. So StringToArrayElem was introduced with a default implementation that relies on Value.
189 lines
4.6 KiB
C++
189 lines
4.6 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 <optional>
|
|
|
|
namespace Pgsql {
|
|
|
|
|
|
template <typename E>
|
|
E StringToArrayElem(std::string_view sv);
|
|
|
|
/** \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;
|
|
operator char() const;
|
|
|
|
bool isString() const;
|
|
|
|
|
|
/// Retrieves an array type value and passes them to an insert iterator.
|
|
///
|
|
/// When the array it self is NULL this function behaves the same as for an empty array. No
|
|
/// elements are given to the insert iterator and the function returns. Use the null() function
|
|
/// to check for NULL values.
|
|
///
|
|
/// \param insert_iter An insert_iterator which is used to add the elements of the array to a container.
|
|
/// \param nullhandling This only affects how NULL elements within the array are handled.
|
|
template <typename E, typename I>
|
|
void getAsArray(I insert_iter, NullHandling nullhandling = NullHandling::Throw) const
|
|
{
|
|
if (m_val == nullptr) {
|
|
return;
|
|
}
|
|
else {
|
|
using value_type = E;
|
|
ArrayParser parser(m_val);
|
|
for (;;) {
|
|
auto res = parser.GetNextElem();
|
|
if (res.ok) {
|
|
if (res.value) {
|
|
insert_iter = StringToArrayElem<E>(*res.value);
|
|
}
|
|
else {
|
|
if (nullhandling == NullHandling::Throw)
|
|
throw std::runtime_error("Unexpected NULL value in array");
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Retrieves an array typed value, and passes the element to the insert iterator
|
|
///
|
|
/// \param value_for_nulls Each time it encounters a NULL element in the array it
|
|
/// will instead pass this value to the insert iterator.
|
|
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) {
|
|
insert_iter = StringToArrayElem<E>(*res.value);
|
|
}
|
|
else {
|
|
insert_iter = value_for_nulls;
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// Retrieves an array typed value, and passes the elements to the insert iterator
|
|
template <typename E, typename I>
|
|
void getAsArrayOfOptional(I insert_iter) 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) {
|
|
insert_iter = StringToArrayElem<E>(*res.value);
|
|
}
|
|
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
|
|
{
|
|
if (m_val == nullptr || *m_val == '\0') return;
|
|
|
|
const char * pos = m_val;
|
|
const char * start = pos;
|
|
for (;;) {
|
|
while (*pos != 0 && *pos != ' ') ++pos;
|
|
// The cast (to prevent warning) should be save as start should always be <= pos
|
|
insert_iter = StringToArrayElem<E>(std::string_view(start, static_cast<size_t>(pos-start)));
|
|
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<<(std::optional<T> &s, const Value &v)
|
|
{
|
|
if (v.null())
|
|
s = std::optional<T>();
|
|
else
|
|
s = v.operator T();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
template <typename E>
|
|
E StringToArrayElem(std::string_view sv)
|
|
{
|
|
std::string str(sv.data(), sv.length());
|
|
Value val(str.c_str(), OidFor<E>::elem());
|
|
E v;
|
|
v << val;
|
|
return v;
|
|
}
|
|
|
|
} // end namespace Pgsql
|
|
|
|
#endif // PGSQL_VALUE_H
|