#pragma once #include #include #include #include #include #include #include #include #include #include #include namespace mysqlxx { class ResultBase; /** Represents a single value read from MySQL. * It doesn't owns the value. It's just a wrapper of a pair (const char *, size_t). * If the UseQueryResult or Connection is destroyed, * or you have read the next Row while using UseQueryResult, then the object is invalidated. * Allows to transform (parse) the value to various data types: * - with getUInt(), getString(), ... (recommended); * - with template function get() that is specialized for multiple data types; * - the template function get also works for all types that can be constructed from Value * (it is an extension point); * - with operator Type() - this is done for compatibility and not recommended because ambiguities possible. * * On parsing error, exception is thrown. * When trying to extract a value that is nullptr, exception is thrown * - use isNull() method to check. * * As time_t is just an alias for integer data type * to allow to write row[0].get(), and expect that the values like '2011-01-01 00:00:00' * will be successfully parsed according to the current time zone, * the getUInt method and the corresponding get<>() methods * are capable of parsing Date and DateTime. */ class Value { public: /** Параметр res_ используется только для генерации подробной информации в исключениях. * Можно передать NULL - тогда подробной информации в исключениях не будет. */ Value(const char * data_, size_t length_, const ResultBase * res_) : m_data(data_), m_length(length_), res(res_) { } /// Получить значение bool. bool getBool() const { if (unlikely(isNull())) throwException("Value is NULL"); return m_length > 0 && m_data[0] != '0'; } /// Получить беззнаковое целое. UInt64 getUInt() const { if (unlikely(isNull())) throwException("Value is NULL"); return readUIntText(m_data, m_length); } /// Получить целое со знаком или дату или дату-время (в unix timestamp согласно текущей тайм-зоне). Int64 getInt() const { return getIntOrDateTime(); } /// Получить число с плавающей запятой. double getDouble() const { if (unlikely(isNull())) throwException("Value is NULL"); return readFloatText(m_data, m_length); } /// Получить дату-время (из значения вида '2011-01-01 00:00:00'). LocalDateTime getDateTime() const { return LocalDateTime(data(), size()); } /// Получить дату (из значения вида '2011-01-01' или '2011-01-01 00:00:00'). LocalDate getDate() const { return LocalDate(data(), size()); } /// Получить строку. std::string getString() const { if (unlikely(isNull())) throwException("Value is NULL"); return std::string(m_data, m_length); } /// Является ли NULL. bool isNull() const { return m_data == nullptr; } /// Для совместимости (используйте вместо этого метод isNull()) bool is_null() const { return isNull(); } /** Получить любой поддерживаемый тип (для шаблонного кода). * Поддерживаются основные типы, а также любые типы с конструктором от Value (для удобства расширения). */ template T get() const; /// Для совместимости. Не рекомендуется к использованию, так как неудобен (часто возникают неоднозначности). template operator T() const { return get(); } const char * data() const { return m_data; } size_t length() const { return m_length; } size_t size() const { return m_length; } bool empty() const { return 0 == m_length; } private: const char * m_data; size_t m_length; const ResultBase * res; bool checkDateTime() const { return (m_length == 10 || m_length == 19) && m_data[4] == '-' && m_data[7] == '-'; } time_t getDateTimeImpl() const { const auto & date_lut = DateLUT::instance(); if (m_length == 10) { return date_lut.makeDate( (m_data[0] - '0') * 1000 + (m_data[1] - '0') * 100 + (m_data[2] - '0') * 10 + (m_data[3] - '0'), (m_data[5] - '0') * 10 + (m_data[6] - '0'), (m_data[8] - '0') * 10 + (m_data[9] - '0')); } else if (m_length == 19) { return date_lut.makeDateTime( (m_data[0] - '0') * 1000 + (m_data[1] - '0') * 100 + (m_data[2] - '0') * 10 + (m_data[3] - '0'), (m_data[5] - '0') * 10 + (m_data[6] - '0'), (m_data[8] - '0') * 10 + (m_data[9] - '0'), (m_data[11] - '0') * 10 + (m_data[12] - '0'), (m_data[14] - '0') * 10 + (m_data[15] - '0'), (m_data[17] - '0') * 10 + (m_data[18] - '0')); } else throwException("Cannot parse DateTime"); return 0; /// avoid warning. } time_t getDateImpl() const { const auto & date_lut = DateLUT::instance(); if (m_length == 10 || m_length == 19) { return date_lut.makeDate( (m_data[0] - '0') * 1000 + (m_data[1] - '0') * 100 + (m_data[2] - '0') * 10 + (m_data[3] - '0'), (m_data[5] - '0') * 10 + (m_data[6] - '0'), (m_data[8] - '0') * 10 + (m_data[9] - '0')); } else throwException("Cannot parse Date"); return 0; /// avoid warning. } Int64 getIntImpl() const { return readIntText(m_data, m_length); } Int64 getIntOrDateTime() const { if (unlikely(isNull())) throwException("Value is NULL"); if (checkDateTime()) return getDateTimeImpl(); else return getIntImpl(); } Int64 getIntOrDate() const { if (unlikely(isNull())) throwException("Value is NULL"); if (checkDateTime()) return getDateImpl(); else { const auto & date_lut = DateLUT::instance(); return date_lut.toDate(getIntImpl()); } } /// Прочитать беззнаковое целое в простом формате из не-0-terminated строки. UInt64 readUIntText(const char * buf, size_t length) const; /// Прочитать знаковое целое в простом формате из не-0-terminated строки. Int64 readIntText(const char * buf, size_t length) const; /// Прочитать число с плавающей запятой в простом формате, с грубым округлением, из не-0-terminated строки. double readFloatText(const char * buf, size_t length) const; /// Выкинуть исключение с подробной информацией [[noreturn]] void throwException(const char * text) const; }; template <> inline bool Value::get() const { return getBool(); } template <> inline char Value::get() const { return getInt(); } template <> inline signed char Value::get() const { return getInt(); } template <> inline unsigned char Value::get() const { return getUInt(); } template <> inline char8_t Value::get() const { return getUInt(); } template <> inline short Value::get() const { return getInt(); } template <> inline unsigned short Value::get() const { return getUInt(); } template <> inline int Value::get() const { return getInt(); } template <> inline unsigned int Value::get() const { return getUInt(); } template <> inline long Value::get() const { return getInt(); } template <> inline unsigned long Value::get() const { return getUInt(); } template <> inline long long Value::get() const { return getInt(); } template <> inline unsigned long long Value::get() const { return getUInt(); } template <> inline float Value::get() const { return getDouble(); } template <> inline double Value::get() const { return getDouble(); } template <> inline std::string Value::get() const { return getString(); } template <> inline LocalDate Value::get() const { return getDate(); } template <> inline LocalDateTime Value::get() const { return getDateTime(); } namespace details { // To avoid stack overflow when converting to type with no appropriate c-tor, // resulting in endless recursive calls from `Value::get()` to `Value::operator T()` to `Value::get()` to ... template >> inline T contructFromValue(const Value & val) { return T(val); } } template inline T Value::get() const { return details::contructFromValue(*this); } inline std::ostream & operator<< (std::ostream & ostr, const Value & x) { return ostr.write(x.data(), x.size()); } }