Loading...
Searching...
No Matches
array-composite.hxx
#if !defined(PQXX_ARRAY_COMPOSITE_HXX)
# define PQXX_ARRAY_COMPOSITE_HXX
# include <cassert>
# include "pqxx/internal/encodings.hxx"
# include "pqxx/strconv.hxx"
namespace pqxx::internal
{
// Find the end of a double-quoted string.
template<encoding_group ENC>
inline std::size_t scan_double_quoted_string(
char const input[], std::size_t size, std::size_t pos)
{
// TODO: find_char<'"', '\\'>().
using scanner = glyph_scanner<ENC>;
auto next{scanner::call(input, size, pos)};
PQXX_ASSUME(next > pos);
bool at_quote{false};
pos = next;
next = scanner::call(input, size, pos);
PQXX_ASSUME(next > pos);
while (pos < size)
{
if (at_quote)
{
if (next - pos == 1 and input[pos] == '"')
{
// We just read a pair of double quotes. Carry on.
at_quote = false;
}
else
{
// We just read one double quote, and now we're at a character that's
// not a second double quote. Ergo, that last character was the
// closing double quote and this is the position right after it.
return pos;
}
}
else if (next - pos == 1)
{
switch (input[pos])
{
case '\\':
// Backslash escape. Skip ahead by one more character.
pos = next;
next = scanner::call(input, size, pos);
PQXX_ASSUME(next > pos);
break;
case '"':
// This is either the closing double quote, or the first of a pair of
// double quotes.
at_quote = true;
break;
}
}
else
{
// Multibyte character. Carry on.
}
pos = next;
next = scanner::call(input, size, pos);
PQXX_ASSUME(next > pos);
}
if (not at_quote)
throw argument_error{
"Missing closing double-quote: " + std::string{input}};
return pos;
}
// TODO: Needs version with caller-supplied buffer.
template<encoding_group ENC>
inline std::string parse_double_quoted_string(
char const input[], std::size_t end, std::size_t pos)
{
std::string output;
// Maximum output size is same as the input size, minus the opening and
// closing quotes. Or in the extreme opposite case, the real number could be
// half that. Usually it'll be a pretty close estimate.
output.reserve(std::size_t(end - pos - 2));
// TODO: Use find_char<...>().
using scanner = glyph_scanner<ENC>;
auto here{scanner::call(input, end, pos)},
next{scanner::call(input, end, here)};
PQXX_ASSUME(here > pos);
PQXX_ASSUME(next > here);
while (here < end - 1)
{
// A backslash here is always an escape. So is a double-quote, since we're
// inside the double-quoted string. In either case, we can just ignore the
// escape character and use the next character. This is the one redeeming
// feature of SQL's escaping system.
if ((next - here == 1) and (input[here] == '\\' or input[here] == '"'))
{
// Skip escape.
here = next;
next = scanner::call(input, end, here);
PQXX_ASSUME(next > here);
}
output.append(input + here, input + next);
here = next;
next = scanner::call(input, end, here);
PQXX_ASSUME(next > here);
}
return output;
}
template<pqxx::internal::encoding_group ENC, char... STOP>
inline std::size_t
scan_unquoted_string(char const input[], std::size_t size, std::size_t pos)
{
using scanner = glyph_scanner<ENC>;
auto next{scanner::call(input, size, pos)};
PQXX_ASSUME(next > pos);
while ((pos < size) and ((next - pos) > 1 or ((input[pos] != STOP) and ...)))
{
pos = next;
next = scanner::call(input, size, pos);
PQXX_ASSUME(next > pos);
}
return pos;
}
template<pqxx::internal::encoding_group ENC>
inline std::string_view
parse_unquoted_string(char const input[], std::size_t end, std::size_t pos)
{
return {&input[pos], end - pos};
}
template<encoding_group ENC, typename T>
inline void parse_composite_field(
std::size_t &index, std::string_view input, std::size_t &pos, T &field,
std::size_t last_field)
{
assert(index <= last_field);
auto next{glyph_scanner<ENC>::call(std::data(input), std::size(input), pos)};
PQXX_ASSUME(next > pos);
if ((next - pos) != 1)
throw conversion_error{"Non-ASCII character in composite-type syntax."};
// Expect a field.
switch (input[pos])
{
case ',':
case ')':
case ']':
// The field is empty, i.e, null.
if constexpr (nullness<T>::has_null)
field = nullness<T>::null();
else
throw conversion_error{
"Can't read composite field " + to_string(index) + ": C++ type " +
type_name<T> + " does not support nulls."};
break;
case '"': {
auto const stop{
scan_double_quoted_string<ENC>(std::data(input), std::size(input), pos)};
PQXX_ASSUME(stop > pos);
auto const text{
parse_double_quoted_string<ENC>(std::data(input), stop, pos)};
field = from_string<T>(text);
pos = stop;
}
break;
default: {
auto const stop{scan_unquoted_string<ENC, ',', ')', ']'>(
std::data(input), std::size(input), pos)};
PQXX_ASSUME(stop >= pos);
field =
from_string<T>(std::string_view{std::data(input) + pos, stop - pos});
pos = stop;
}
break;
}
// Expect a comma or a closing parenthesis.
next = glyph_scanner<ENC>::call(std::data(input), std::size(input), pos);
PQXX_ASSUME(next > pos);
if ((next - pos) != 1)
throw conversion_error{
"Unexpected non-ASCII character after composite field: " +
std::string{input}};
if (index < last_field)
{
if (input[pos] != ',')
throw conversion_error{
"Found '" + std::string{input[pos]} +
"' in composite value where comma was expected: " + std::data(input)};
}
else
{
if (input[pos] == ',')
throw conversion_error{
"Composite value contained more fields than the expected " +
to_string(last_field) + ": " + std::data(input)};
if (input[pos] != ')' and input[pos] != ']')
throw conversion_error{
"Composite value has unexpected characters where closing parenthesis "
"was expected: " +
std::string{input}};
if (next != std::size(input))
throw conversion_error{
"Composite value has unexpected text after closing parenthesis: " +
std::string{input}};
}
pos = next;
++index;
}
template<typename T>
using composite_field_parser = void (*)(
std::size_t &index, std::string_view input, std::size_t &pos, T &field,
std::size_t last_field);
template<typename T>
composite_field_parser<T> specialize_parse_composite_field(encoding_group enc)
{
switch (enc)
{
case encoding_group::MONOBYTE:
return parse_composite_field<encoding_group::MONOBYTE>;
case encoding_group::BIG5:
return parse_composite_field<encoding_group::BIG5>;
case encoding_group::EUC_CN:
return parse_composite_field<encoding_group::EUC_CN>;
case encoding_group::EUC_JP:
return parse_composite_field<encoding_group::EUC_JP>;
case encoding_group::EUC_KR:
return parse_composite_field<encoding_group::EUC_KR>;
case encoding_group::EUC_TW:
return parse_composite_field<encoding_group::EUC_TW>;
case encoding_group::GB18030:
return parse_composite_field<encoding_group::GB18030>;
case encoding_group::GBK: return parse_composite_field<encoding_group::GBK>;
case encoding_group::JOHAB:
return parse_composite_field<encoding_group::JOHAB>;
case encoding_group::MULE_INTERNAL:
return parse_composite_field<encoding_group::MULE_INTERNAL>;
case encoding_group::SJIS:
return parse_composite_field<encoding_group::SJIS>;
case encoding_group::UHC: return parse_composite_field<encoding_group::UHC>;
case encoding_group::UTF8:
return parse_composite_field<encoding_group::UTF8>;
}
throw internal_error{concat("Unexpected encoding group code: ", enc, ".")};
}
template<typename T>
inline std::size_t size_composite_field_buffer(T const &field)
{
if constexpr (is_unquoted_safe<T>)
{
// Safe to copy, without quotes or escaping. Drop the terminating zero.
return size_buffer(field) - 1;
}
else
{
// + Opening quote.
// + Field budget.
// - Terminating zero.
// + Escaping for each byte in the field's string representation.
// - Escaping for terminating zero.
// + Closing quote.
return 1 + 2 * (size_buffer(field) - 1) + 1;
}
}
template<typename T>
inline void write_composite_field(char *&pos, char *end, T const &field)
{
if constexpr (is_unquoted_safe<T>)
{
// No need for quoting or escaping. Convert it straight into its final
// place in the buffer, and "backspace" the trailing zero.
pos = string_traits<T>::into_buf(pos, end, field) - 1;
}
else
{
// The field may need escaping, which means we need an intermediate buffer.
// To avoid allocating that at run time, we use the end of the buffer that
// we have.
auto const budget{size_buffer(field)};
*pos++ = '"';
// Now escape buf into its final position.
for (char const c : string_traits<T>::to_buf(end - budget, end, field))
{
if ((c == '"') or (c == '\\'))
*pos++ = '\\';
*pos++ = c;
}
*pos++ = '"';
}
*pos++ = ',';
}
} // namespace pqxx::internal
#endif
pqxx::argument_error
Invalid argument passed to libpqxx, similar to std::invalid_argument.
Definition except.hxx:266
pqxx::conversion_error
Value conversion failed, e.g. when converting "Hello" to int.
Definition except.hxx:283
pqxx::internal_error
Internal error in libpqxx library.
Definition except.hxx:242
pqxx::internal
Internal items for libpqxx' own use. Do not use these yourself.
Definition encodings.cxx:33
pqxx::internal::concat
std::string concat(TYPE... item)
Efficiently combine a bunch of items into one big string.
Definition concat.hxx:31
pqxx::internal::parse_composite_field
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:180
pqxx::internal::size_composite_field_buffer
std::size_t size_composite_field_buffer(T const &field)
Conservatively estimate buffer size needed for a composite field.
Definition array-composite.hxx:309
pqxx::internal::parse_unquoted_string
std::string_view 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:149
pqxx::internal::scan_double_quoted_string
std::size_t scan_double_quoted_string(char const input[], std::size_t size, std::size_t pos)
Definition array-composite.hxx:20
pqxx::internal::scan_unquoted_string
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
pqxx::internal::specialize_parse_composite_field
composite_field_parser< T > specialize_parse_composite_field(encoding_group enc)
Look up implementation of parse_composite_field for ENC.
Definition array-composite.hxx:274
pqxx::internal::parse_double_quoted_string
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
pqxx::internal::composite_field_parser
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:267
pqxx::type_name
std::string const type_name
A human-readable name for a type, used in error messages and such.
Definition strconv.hxx:80
pqxx::is_unquoted_safe
constexpr bool is_unquoted_safe
Can we use this type in arrays and composite types without quoting them?
Definition strconv.hxx:555
pqxx::size_buffer
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:526
pqxx::from_string
T from_string(field const &value)
Convert a field's value to type T.
Definition field.hxx:548
pqxx::internal::glyph_scanner
Wrapper struct template for "find next glyph" functions.
Definition encodings.hxx:143
pqxx::internal::glyph_scanner::call
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.
pqxx::nullness::null
static TYPE null()
Return a null value.
pqxx::nullness::has_null
static bool has_null
Does this type have a null value?
Definition strconv.hxx:93
pqxx::string_traits::to_buf
static zview to_buf(char *begin, char *end, TYPE const &value)
Return a string_view representing value, plus terminating zero.
pqxx::string_traits::into_buf
static char * into_buf(char *begin, char *end, TYPE const &value)
Write value's string representation into buffer at begin.