libpqxx
The C++ client library for PostgreSQL
Loading...
Searching...
No Matches
array-composite.hxx
1#if !defined(PQXX_ARRAY_COMPOSITE_HXX)
2# define PQXX_ARRAY_COMPOSITE_HXX
3
4# include <cassert>
5
6# include "pqxx/internal/encodings.hxx"
7# include "pqxx/strconv.hxx"
8
9namespace pqxx::internal
10{
11// Find the end of a double-quoted string.
19template<encoding_group ENC>
20inline std::size_t scan_double_quoted_string(
21 char const input[], std::size_t size, std::size_t pos)
22{
23 // TODO: find_char<'"', '\\'>().
24 using scanner = glyph_scanner<ENC>;
25 auto next{scanner::call(input, size, pos)};
26 PQXX_ASSUME(next > pos);
27 bool at_quote{false};
28 pos = next;
29 next = scanner::call(input, size, pos);
30 PQXX_ASSUME(next > pos);
31 while (pos < size)
32 {
33 if (at_quote)
34 {
35 if (next - pos == 1 and input[pos] == '"')
36 {
37 // We just read a pair of double quotes. Carry on.
38 at_quote = false;
39 }
40 else
41 {
42 // We just read one double quote, and now we're at a character that's
43 // not a second double quote. Ergo, that last character was the
44 // closing double quote and this is the position right after it.
45 return pos;
46 }
47 }
48 else if (next - pos == 1)
49 {
50 switch (input[pos])
51 {
52 case '\\':
53 // Backslash escape. Skip ahead by one more character.
54 pos = next;
55 next = scanner::call(input, size, pos);
56 PQXX_ASSUME(next > pos);
57 break;
58
59 case '"':
60 // This is either the closing double quote, or the first of a pair of
61 // double quotes.
62 at_quote = true;
63 break;
64 }
65 }
66 else
67 {
68 // Multibyte character. Carry on.
69 }
70 pos = next;
71 next = scanner::call(input, size, pos);
72 PQXX_ASSUME(next > pos);
73 }
74 if (not at_quote)
75 throw argument_error{
76 "Missing closing double-quote: " + std::string{input}};
77 return pos;
78}
79
80
81// TODO: Needs version with caller-supplied buffer.
83template<encoding_group ENC>
84inline std::string parse_double_quoted_string(
85 char const input[], std::size_t end, std::size_t pos)
86{
87 std::string output;
88 // Maximum output size is same as the input size, minus the opening and
89 // closing quotes. Or in the extreme opposite case, the real number could be
90 // half that. Usually it'll be a pretty close estimate.
91 output.reserve(std::size_t(end - pos - 2));
92
93 // TODO: Use find_char<...>().
94 using scanner = glyph_scanner<ENC>;
95 auto here{scanner::call(input, end, pos)},
96 next{scanner::call(input, end, here)};
97 PQXX_ASSUME(here > pos);
98 PQXX_ASSUME(next > here);
99 while (here < end - 1)
100 {
101 // A backslash here is always an escape. So is a double-quote, since we're
102 // inside the double-quoted string. In either case, we can just ignore the
103 // escape character and use the next character. This is the one redeeming
104 // feature of SQL's escaping system.
105 if ((next - here == 1) and (input[here] == '\\' or input[here] == '"'))
106 {
107 // Skip escape.
108 here = next;
109 next = scanner::call(input, end, here);
110 PQXX_ASSUME(next > here);
111 }
112 output.append(input + here, input + next);
113 here = next;
114 next = scanner::call(input, end, here);
115 PQXX_ASSUME(next > here);
116 }
117 return output;
118}
119
120
122
129template<pqxx::internal::encoding_group ENC, char... STOP>
130inline std::size_t
131scan_unquoted_string(char const input[], std::size_t size, std::size_t pos)
132{
133 using scanner = glyph_scanner<ENC>;
134 auto next{scanner::call(input, size, pos)};
135 PQXX_ASSUME(next > pos);
136 while ((pos < size) and ((next - pos) > 1 or ((input[pos] != STOP) and ...)))
137 {
138 pos = next;
139 next = scanner::call(input, size, pos);
140 PQXX_ASSUME(next > pos);
141 }
142 return pos;
143}
144
145
146// XXX: Retire this. Just construct a string_view directly now!
148template<pqxx::internal::encoding_group ENC>
149inline std::string
150parse_unquoted_string(char const input[], std::size_t end, std::size_t pos)
151{
152 return {&input[pos], end - pos};
153}
154
155
157
180template<encoding_group ENC, typename T>
182 std::size_t &index, std::string_view input, std::size_t &pos, T &field,
183 std::size_t last_field)
184{
185 assert(index <= last_field);
186 auto next{glyph_scanner<ENC>::call(std::data(input), std::size(input), pos)};
187 PQXX_ASSUME(next > pos);
188 if ((next - pos) != 1)
189 throw conversion_error{"Non-ASCII character in composite-type syntax."};
190
191 // Expect a field.
192 switch (input[pos])
193 {
194 case ',':
195 case ')':
196 case ']':
197 // The field is empty, i.e, null.
198 if constexpr (nullness<T>::has_null)
199 field = nullness<T>::null();
200 else
201 throw conversion_error{
202 "Can't read composite field " + to_string(index) + ": C++ type " +
203 type_name<T> + " does not support nulls."};
204 break;
205
206 case '"': {
207 auto const stop{
208 scan_double_quoted_string<ENC>(std::data(input), std::size(input), pos)};
209 PQXX_ASSUME(stop > pos);
210 auto const text{
211 parse_double_quoted_string<ENC>(std::data(input), stop, pos)};
212 field = from_string<T>(text);
213 pos = stop;
214 }
215 break;
216
217 default: {
219 std::data(input), std::size(input), pos)};
220 PQXX_ASSUME(stop >= pos);
221 field =
222 from_string<T>(std::string_view{std::data(input) + pos, stop - pos});
223 pos = stop;
224 }
225 break;
226 }
227
228 // Expect a comma or a closing parenthesis.
229 next = glyph_scanner<ENC>::call(std::data(input), std::size(input), pos);
230 PQXX_ASSUME(next > pos);
231
232 if ((next - pos) != 1)
233 throw conversion_error{
234 "Unexpected non-ASCII character after composite field: " +
235 std::string{input}};
236
237 if (index < last_field)
238 {
239 if (input[pos] != ',')
240 throw conversion_error{
241 "Found '" + std::string{input[pos]} +
242 "' in composite value where comma was expected: " + std::data(input)};
243 }
244 else
245 {
246 if (input[pos] == ',')
247 throw conversion_error{
248 "Composite value contained more fields than the expected " +
249 to_string(last_field) + ": " + std::data(input)};
250 if (input[pos] != ')' and input[pos] != ']')
251 throw conversion_error{
252 "Composite value has unexpected characters where closing parenthesis "
253 "was expected: " +
254 std::string{input}};
255 if (next != std::size(input))
256 throw conversion_error{
257 "Composite value has unexpected text after closing parenthesis: " +
258 std::string{input}};
259 }
260
261 pos = next;
262 ++index;
263}
264
265
267template<typename T>
268using composite_field_parser = void (*)(
269 std::size_t &index, std::string_view input, std::size_t &pos, T &field,
270 std::size_t last_field);
271
272
274template<typename T>
276{
277 switch (enc)
278 {
279 case encoding_group::MONOBYTE:
281 case encoding_group::BIG5:
283 case encoding_group::EUC_CN:
285 case encoding_group::EUC_JP:
287 case encoding_group::EUC_KR:
289 case encoding_group::EUC_TW:
291 case encoding_group::GB18030:
293 case encoding_group::GBK: return parse_composite_field<encoding_group::GBK>;
294 case encoding_group::JOHAB:
296 case encoding_group::MULE_INTERNAL:
298 case encoding_group::SJIS:
300 case encoding_group::UHC: return parse_composite_field<encoding_group::UHC>;
301 case encoding_group::UTF8:
303 }
304 throw internal_error{concat("Unexpected encoding group code: ", enc, ".")};
305}
306
307
309template<typename T>
310inline std::size_t size_composite_field_buffer(T const &field)
311{
312 if constexpr (is_unquoted_safe<T>)
313 {
314 // Safe to copy, without quotes or escaping. Drop the terminating zero.
315 return size_buffer(field) - 1;
316 }
317 else
318 {
319 // + Opening quote.
320 // + Field budget.
321 // - Terminating zero.
322 // + Escaping for each byte in the field's string representation.
323 // - Escaping for terminating zero.
324 // + Closing quote.
325 return 1 + 2 * (size_buffer(field) - 1) + 1;
326 }
327}
328
329
330template<typename T>
331inline void write_composite_field(char *&pos, char *end, T const &field)
332{
333 if constexpr (is_unquoted_safe<T>)
334 {
335 // No need for quoting or escaping. Convert it straight into its final
336 // place in the buffer, and "backspace" the trailing zero.
337 pos = string_traits<T>::into_buf(pos, end, field) - 1;
338 }
339 else
340 {
341 // The field may need escaping, which means we need an intermediate buffer.
342 // To avoid allocating that at run time, we use the end of the buffer that
343 // we have.
344 auto const budget{size_buffer(field)};
345 *pos++ = '"';
346
347 // Now escape buf into its final position.
348 for (char const c : string_traits<T>::to_buf(end - budget, end, field))
349 {
350 if ((c == '"') or (c == '\\'))
351 *pos++ = '\\';
352
353 *pos++ = c;
354 }
355
356 *pos++ = '"';
357 }
358
359 *pos++ = ',';
360}
361} // namespace pqxx::internal
362#endif
Invalid argument passed to libpqxx, similar to std::invalid_argument.
Definition except.hxx:266
Value conversion failed, e.g. when converting "Hello" to int.
Definition except.hxx:283
Internal error in libpqxx library.
Definition except.hxx:242
Internal items for libpqxx' own use. Do not use these yourself.
Definition encodings.cxx:33
std::string concat(TYPE... item)
Efficiently combine a bunch of items into one big string.
Definition concat.hxx:31
void parse_composite_field(std::size_t &index, std::string_view input, std::size_t &pos, T &field, std::size_t last_field)
Parse a field of a composite-type value.
Definition array-composite.hxx:181
std::size_t size_composite_field_buffer(T const &field)
Conservatively estimate buffer size needed for a composite field.
Definition array-composite.hxx:310
std::string parse_unquoted_string(char const input[], std::size_t end, std::size_t pos)
Parse an unquoted array entry or cfield of a composite-type field.
Definition array-composite.hxx:150
std::size_t scan_double_quoted_string(char const input[], std::size_t size, std::size_t pos)
Definition array-composite.hxx:20
std::size_t scan_unquoted_string(char const input[], std::size_t size, std::size_t pos)
Find the end of an unquoted string in an array or composite-type value.
Definition array-composite.hxx:131
composite_field_parser< T > specialize_parse_composite_field(encoding_group enc)
Look up implementation of parse_composite_field for ENC.
Definition array-composite.hxx:275
std::string parse_double_quoted_string(char const input[], std::size_t end, std::size_t pos)
Un-quote and un-escape a double-quoted SQL string.
Definition array-composite.hxx:84
void(*)( std::size_t &index, std::string_view input, std::size_t &pos, T &field, std::size_t last_field) composite_field_parser
Pointer to an encoding-specific specialisation of parse_composite_field.
Definition array-composite.hxx:268
std::string const type_name
A human-readable name for a type, used in error messages and such.
Definition strconv.hxx:80
std::size_t size_buffer(TYPE const &...value) noexcept
Estimate how much buffer space is needed to represent values as a string.
Definition strconv.hxx:525
T from_string(field const &value)
Convert a field's value to type T.
Definition field.hxx:532
constexpr bool is_unquoted_safe
Can we use this type in arrays and composite types without quoting them?
Definition strconv.hxx:554
Wrapper struct template for "find next glyph" functions.
Definition encodings.hxx:143
static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start)
Find the next glyph in buffer after position start.
Traits describing a type's "null value," if any.
Definition strconv.hxx:91
static zview to_buf(char *begin, char *end, TYPE const &value)
Return a string_view representing value, plus terminating zero.
static char * into_buf(char *begin, char *end, TYPE const &value)
Write value's string representation into buffer at begin.