1 | #pragma once |
2 | |
3 | #include <string.h> |
4 | #include <stdio.h> |
5 | #include <time.h> |
6 | #include <math.h> |
7 | |
8 | #include <string> |
9 | #include <limits> |
10 | |
11 | #include <common/preciseExp10.h> |
12 | #include <common/Types.h> |
13 | #include <common/DateLUT.h> |
14 | |
15 | #include <mysqlxx/Types.h> |
16 | #include <common/LocalDateTime.h> |
17 | |
18 | |
19 | namespace mysqlxx |
20 | { |
21 | |
22 | |
23 | class ResultBase; |
24 | |
25 | |
26 | /** Представляет одно значение, считанное из MySQL. |
27 | * Объект сам не хранит данные, а является всего лишь обёрткой над парой (const char *, size_t). |
28 | * Если уничтожить UseQueryResult/StoreQueryResult или Connection, |
29 | * или считать следующий Row при использовании UseQueryResult, то объект станет некорректным. |
30 | * Позволяет преобразовать значение (распарсить) в различные типы данных: |
31 | * - с помощью функций вида getUInt(), getString(), ... (рекомендуется); |
32 | * - с помощью шаблонной функции get<Type>(), которая специализирована для многих типов (для шаблонного кода); |
33 | * - шаблонная функция get<Type> работает также для всех типов, у которых есть конструктор из Value |
34 | * (это сделано для возможности расширения); |
35 | * - с помощью operator Type() - но этот метод реализован лишь для совместимости и не рекомендуется |
36 | * к использованию, так как неудобен (часто возникают неоднозначности). |
37 | * |
38 | * При ошибке парсинга, выкидывается исключение. |
39 | * При попытке достать значение, которое равно nullptr, выкидывается исключение |
40 | * - используйте метод isNull() для проверки. |
41 | * |
42 | * Во всех распространённых системах, time_t - это всего лишь typedef от Int64 или Int32. |
43 | * Для того, чтобы можно было писать row[0].get<time_t>(), ожидая, что значение вида '2011-01-01 00:00:00' |
44 | * корректно распарсится согласно текущей тайм-зоне, сделано так, что метод getUInt и соответствующие методы get<>() |
45 | * также умеют парсить дату и дату-время. |
46 | */ |
47 | class Value |
48 | { |
49 | public: |
50 | /** Параметр res_ используется только для генерации подробной информации в исключениях. |
51 | * Можно передать NULL - тогда подробной информации в исключениях не будет. |
52 | */ |
53 | Value(const char * data_, size_t length_, const ResultBase * res_) : m_data(data_), m_length(length_), res(res_) |
54 | { |
55 | } |
56 | |
57 | /// Получить значение bool. |
58 | bool getBool() const |
59 | { |
60 | if (unlikely(isNull())) |
61 | throwException("Value is NULL" ); |
62 | |
63 | return m_length > 0 && m_data[0] != '0'; |
64 | } |
65 | |
66 | /// Получить беззнаковое целое. |
67 | UInt64 getUInt() const |
68 | { |
69 | if (unlikely(isNull())) |
70 | throwException("Value is NULL" ); |
71 | |
72 | return readUIntText(m_data, m_length); |
73 | } |
74 | |
75 | /// Получить целое со знаком или дату или дату-время (в unix timestamp согласно текущей тайм-зоне). |
76 | Int64 getInt() const |
77 | { |
78 | return getIntOrDateTime(); |
79 | } |
80 | |
81 | /// Получить число с плавающей запятой. |
82 | double getDouble() const |
83 | { |
84 | if (unlikely(isNull())) |
85 | throwException("Value is NULL" ); |
86 | |
87 | return readFloatText(m_data, m_length); |
88 | } |
89 | |
90 | /// Получить дату-время (из значения вида '2011-01-01 00:00:00'). |
91 | LocalDateTime getDateTime() const |
92 | { |
93 | return LocalDateTime(data(), size()); |
94 | } |
95 | |
96 | /// Получить дату (из значения вида '2011-01-01' или '2011-01-01 00:00:00'). |
97 | LocalDate getDate() const |
98 | { |
99 | return LocalDate(data(), size()); |
100 | } |
101 | |
102 | /// Получить строку. |
103 | std::string getString() const |
104 | { |
105 | if (unlikely(isNull())) |
106 | throwException("Value is NULL" ); |
107 | |
108 | return std::string(m_data, m_length); |
109 | } |
110 | |
111 | /// Является ли NULL. |
112 | bool isNull() const |
113 | { |
114 | return m_data == nullptr; |
115 | } |
116 | |
117 | /// Для совместимости (используйте вместо этого метод isNull()) |
118 | bool is_null() const { return isNull(); } |
119 | |
120 | /** Получить любой поддерживаемый тип (для шаблонного кода). |
121 | * Поддерживаются основные типы, а также любые типы с конструктором от Value (для удобства расширения). |
122 | */ |
123 | template <typename T> T get() const; |
124 | |
125 | /// Для совместимости. Не рекомендуется к использованию, так как неудобен (часто возникают неоднозначности). |
126 | template <typename T> operator T() const { return get<T>(); } |
127 | |
128 | const char * data() const { return m_data; } |
129 | size_t length() const { return m_length; } |
130 | size_t size() const { return m_length; } |
131 | bool empty() const { return 0 == m_length; } |
132 | |
133 | private: |
134 | const char * m_data; |
135 | size_t m_length; |
136 | const ResultBase * res; |
137 | |
138 | |
139 | bool checkDateTime() const |
140 | { |
141 | return (m_length == 10 || m_length == 19) && m_data[4] == '-' && m_data[7] == '-'; |
142 | } |
143 | |
144 | |
145 | time_t getDateTimeImpl() const |
146 | { |
147 | const auto & date_lut = DateLUT::instance(); |
148 | |
149 | if (m_length == 10) |
150 | { |
151 | return date_lut.makeDate( |
152 | (m_data[0] - '0') * 1000 + (m_data[1] - '0') * 100 + (m_data[2] - '0') * 10 + (m_data[3] - '0'), |
153 | (m_data[5] - '0') * 10 + (m_data[6] - '0'), |
154 | (m_data[8] - '0') * 10 + (m_data[9] - '0')); |
155 | } |
156 | else if (m_length == 19) |
157 | { |
158 | return date_lut.makeDateTime( |
159 | (m_data[0] - '0') * 1000 + (m_data[1] - '0') * 100 + (m_data[2] - '0') * 10 + (m_data[3] - '0'), |
160 | (m_data[5] - '0') * 10 + (m_data[6] - '0'), |
161 | (m_data[8] - '0') * 10 + (m_data[9] - '0'), |
162 | (m_data[11] - '0') * 10 + (m_data[12] - '0'), |
163 | (m_data[14] - '0') * 10 + (m_data[15] - '0'), |
164 | (m_data[17] - '0') * 10 + (m_data[18] - '0')); |
165 | } |
166 | else |
167 | throwException("Cannot parse DateTime" ); |
168 | |
169 | return 0; /// чтобы не было warning-а. |
170 | } |
171 | |
172 | |
173 | time_t getDateImpl() const |
174 | { |
175 | const auto & date_lut = DateLUT::instance(); |
176 | |
177 | if (m_length == 10 || m_length == 19) |
178 | { |
179 | return date_lut.makeDate( |
180 | (m_data[0] - '0') * 1000 + (m_data[1] - '0') * 100 + (m_data[2] - '0') * 10 + (m_data[3] - '0'), |
181 | (m_data[5] - '0') * 10 + (m_data[6] - '0'), |
182 | (m_data[8] - '0') * 10 + (m_data[9] - '0')); |
183 | } |
184 | else |
185 | throwException("Cannot parse Date" ); |
186 | |
187 | return 0; /// чтобы не было warning-а. |
188 | } |
189 | |
190 | |
191 | Int64 getIntImpl() const |
192 | { |
193 | return readIntText(m_data, m_length); |
194 | } |
195 | |
196 | |
197 | Int64 getIntOrDateTime() const |
198 | { |
199 | if (unlikely(isNull())) |
200 | throwException("Value is NULL" ); |
201 | |
202 | if (checkDateTime()) |
203 | return getDateTimeImpl(); |
204 | else |
205 | return getIntImpl(); |
206 | } |
207 | |
208 | |
209 | Int64 getIntOrDate() const |
210 | { |
211 | if (unlikely(isNull())) |
212 | throwException("Value is NULL" ); |
213 | |
214 | if (checkDateTime()) |
215 | return getDateImpl(); |
216 | else |
217 | { |
218 | const auto & date_lut = DateLUT::instance(); |
219 | return date_lut.toDate(getIntImpl()); |
220 | } |
221 | } |
222 | |
223 | |
224 | /// Прочитать беззнаковое целое в простом формате из не-0-terminated строки. |
225 | UInt64 readUIntText(const char * buf, size_t length) const; |
226 | |
227 | /// Прочитать знаковое целое в простом формате из не-0-terminated строки. |
228 | Int64 readIntText(const char * buf, size_t length) const; |
229 | |
230 | /// Прочитать число с плавающей запятой в простом формате, с грубым округлением, из не-0-terminated строки. |
231 | double readFloatText(const char * buf, size_t length) const; |
232 | |
233 | /// Выкинуть исключение с подробной информацией |
234 | void throwException(const char * text) const; |
235 | }; |
236 | |
237 | |
238 | template <> inline bool Value::get<bool >() const { return getBool(); } |
239 | template <> inline char Value::get<char >() const { return getInt(); } |
240 | template <> inline signed char Value::get<signed char >() const { return getInt(); } |
241 | template <> inline unsigned char Value::get<unsigned char >() const { return getUInt(); } |
242 | template <> inline short Value::get<short >() const { return getInt(); } |
243 | template <> inline unsigned short Value::get<unsigned short >() const { return getUInt(); } |
244 | template <> inline int Value::get<int >() const { return getInt(); } |
245 | template <> inline unsigned int Value::get<unsigned int >() const { return getUInt(); } |
246 | template <> inline long Value::get<long >() const { return getInt(); } |
247 | template <> inline unsigned long Value::get<unsigned long >() const { return getUInt(); } |
248 | template <> inline long long Value::get<long long >() const { return getInt(); } |
249 | template <> inline unsigned long long Value::get<unsigned long long >() const { return getUInt(); } |
250 | template <> inline float Value::get<float >() const { return getDouble(); } |
251 | template <> inline double Value::get<double >() const { return getDouble(); } |
252 | template <> inline std::string Value::get<std::string >() const { return getString(); } |
253 | template <> inline LocalDate Value::get<LocalDate >() const { return getDate(); } |
254 | template <> inline LocalDateTime Value::get<LocalDateTime >() const { return getDateTime(); } |
255 | |
256 | template <typename T> inline T Value::get() const { return T(*this); } |
257 | |
258 | |
259 | inline std::ostream & operator<< (std::ostream & ostr, const Value & x) |
260 | { |
261 | return ostr.write(x.data(), x.size()); |
262 | } |
263 | |
264 | |
265 | } |
266 | |