#ifndef MYSQLXX_STRING_H #define MYSQLXX_STRING_H #include #include #include #include #include #include #include #include /// Обрезать длинный запрос до указанной длины для текста исключения. #define MYSQLXX_QUERY_PREVIEW_LENGTH 1000 namespace mysqlxx { class ResultBase; /** Представляет одно значение, считанное из MySQL. * Объект сам не хранит данные, а является всего лишь обёрткой над парой (const char *, size_t). * Если уничтожить UseQueryResult/StoreQueryResult или Connection, * или считать следующий Row при использовании UseQueryResult, то объект станет некорректным. * Позволяет преобразовать значение (распарсить) в различные типы данных: * - с помощью функций вида getUInt(), getString(), ... (рекомендуется); * - с помощью шаблонной функции get(), которая специализирована для многих типов (для шаблонного кода); * - шаблонная функция get работает также для всех типов, у которых есть конструктор из String * (это сделано для возможности расширения); * - с помощью operator Type() - но этот метод реализован лишь для совместимости и не рекомендуется * к использованию, так как неудобен (часто возникают неоднозначности). * * При ошибке парсинга, выкидывается исключение. * При попытке достать значение, которое равно NULL, выкидывается исключение * - используйте метод isNull() для проверки. * * Во всех распространённых системах, time_t - это всего лишь typedef от Int64 или Int32. * Для того, чтобы можно было писать row[0].get(), ожидая, что значение вида '2011-01-01 00:00:00' * корректно распарсится согласно текущей тайм-зоне, сделано так, что метод getUInt и соответствующие методы get<>() * также умеют парсить дату и дату-время. */ class String { public: /** Параметр res_ используется только для генерации подробной информации в исключениях. * Можно передать NULL - тогда подробной информации в исключениях не будет. */ String(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'). DateTime getDateTime() const { return DateTime(data(), size()); } /// Получить дату (из значения вида '2011-01-01' или '2011-01-01 00:00:00'). Date getDate() const { return Date(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 == NULL; } /// Для совместимости (используйте вместо этого метод isNull()) bool is_null() const { return isNull(); } /** Получить любой поддерживаемый тип (для шаблонного кода). * Поддерживаются основные типы, а также любые типы с конструктором от String (для удобства расширения). */ 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 { Yandex::DateLUTSingleton & date_lut = Yandex::DateLUTSingleton::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; /// чтобы не было warning-а. } time_t getDateImpl() const { Yandex::DateLUTSingleton & date_lut = Yandex::DateLUTSingleton::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"); } 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 { Yandex::DateLUTSingleton & date_lut = Yandex::DateLUTSingleton::instance(); return date_lut.toDate(getIntImpl()); } } /// Прочитать беззнаковое целое в простом формате из не-0-terminated строки. UInt64 readUIntText(const char * buf, size_t length) const { UInt64 x = 0; const char * end = buf + length; while (buf != end) { switch (*buf) { case '+': break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': x *= 10; x += *buf - '0'; break; default: throwException("Cannot parse unsigned integer"); } ++buf; } return x; } /// Прочитать знаковое целое в простом формате из не-0-terminated строки. Int64 readIntText(const char * buf, size_t length) const { bool negative = false; Int64 x = 0; const char * end = buf + length; while (buf != end) { switch (*buf) { case '+': break; case '-': negative = true; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': x *= 10; x += *buf - '0'; break; default: throwException("Cannot parse signed integer"); } ++buf; } if (negative) x = -x; return x; } /// Прочитать число с плавающей запятой в простом формате, с грубым округлением, из не-0-terminated строки. double readFloatText(const char * buf, size_t length) const { bool negative = false; double x = 0; bool after_point = false; double power_of_ten = 1; const char * end = buf + length; while (buf != end) { switch (*buf) { case '+': break; case '-': negative = true; break; case '.': after_point = true; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (after_point) { power_of_ten /= 10; x += (*buf - '0') * power_of_ten; } else { x *= 10; x += *buf - '0'; } break; case 'e': case 'E': { ++buf; Int32 exponent = readIntText(buf, end - buf); if (exponent == 0) { if (negative) x = -x; return x; } else if (exponent > 0) { for (Int32 i = 0; i < exponent; ++i) x *= 10; if (negative) x = -x; return x; } else { for (Int32 i = 0; i < exponent; ++i) x /= 10; if (negative) x = -x; return x; } } case 'i': case 'I': x = std::numeric_limits::infinity(); if (negative) x = -x; return x; case 'n': case 'N': x = std::numeric_limits::quiet_NaN(); return x; default: throwException("Cannot parse floating point number"); } ++buf; } if (negative) x = -x; return x; } /// Выкинуть исключение с подробной информацией void throwException(const char * text) const; }; template <> inline bool String::get() const { return getBool(); } template <> inline char String::get() const { return getInt(); } template <> inline signed char String::get() const { return getInt(); } template <> inline unsigned char String::get() const { return getUInt(); } template <> inline short String::get() const { return getInt(); } template <> inline unsigned short String::get() const { return getUInt(); } template <> inline int String::get() const { return getInt(); } template <> inline unsigned int String::get() const { return getUInt(); } template <> inline long String::get() const { return getInt(); } template <> inline unsigned long String::get() const { return getUInt(); } template <> inline long long String::get() const { return getInt(); } template <> inline unsigned long long String::get() const { return getUInt(); } template <> inline float String::get() const { return getDouble(); } template <> inline double String::get() const { return getDouble(); } template <> inline std::string String::get() const { return getString(); } template <> inline Date String::get() const { return getDate(); } template <> inline DateTime String::get() const { return getDateTime(); } template <> inline Yandex::VisitID_t String::get() const { return Yandex::VisitID_t(getUInt()); } template inline T String::get() const { return T(*this); } inline std::ostream & operator<< (std::ostream & ostr, const String & x) { return ostr.write(x.data(), x.size()); } } #endif