composite.hxx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. #ifndef PQXX_H_COMPOSITE
  2. #define PQXX_H_COMPOSITE
  3. #if !defined(PQXX_HEADER_PRE)
  4. # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
  5. #endif
  6. #include "pqxx/internal/array-composite.hxx"
  7. #include "pqxx/internal/concat.hxx"
  8. #include "pqxx/util.hxx"
  9. namespace pqxx
  10. {
  11. /// Parse a string representation of a value of a composite type.
  12. /** @warning This code is still experimental. Use with care.
  13. *
  14. * You may use this as a helper while implementing your own @ref string_traits
  15. * for a composite type.
  16. *
  17. * This function interprets `text` as the string representation of a value of
  18. * some composite type, and sets each of `fields` to the respective values of
  19. * its fields. The field types must be copy-assignable.
  20. *
  21. * The number of fields must match the number of fields in the composite type,
  22. * and there must not be any other text in the input. The function is meant to
  23. * handle any value string that the backend can produce, but not necessarily
  24. * every valid alternative spelling.
  25. *
  26. * Fields in composite types can be null. When this happens, the C++ type of
  27. * the corresponding field reference must be of a type that can handle nulls.
  28. * If you are working with a type that does not have an inherent null value,
  29. * such as e.g. `int`, consider using `std::optional`.
  30. */
  31. template<typename... T>
  32. inline void parse_composite(
  33. pqxx::internal::encoding_group enc, std::string_view text, T &...fields)
  34. {
  35. static_assert(sizeof...(fields) > 0);
  36. auto const scan{pqxx::internal::get_glyph_scanner(enc)};
  37. auto const data{std::data(text)};
  38. auto const size{std::size(text)};
  39. if (size == 0)
  40. throw conversion_error{"Cannot parse composite value from empty string."};
  41. std::size_t here{0}, next{scan(data, size, here)};
  42. if (next != 1 or data[here] != '(')
  43. throw conversion_error{
  44. internal::concat("Invalid composite value string: ", text)};
  45. here = next;
  46. constexpr auto num_fields{sizeof...(fields)};
  47. std::size_t index{0};
  48. (pqxx::internal::parse_composite_field(
  49. index, text, here, fields, scan, num_fields - 1),
  50. ...);
  51. if (here != std::size(text))
  52. throw conversion_error{internal::concat(
  53. "Composite value did not end at the closing parenthesis: '", text,
  54. "'.")};
  55. if (text[here - 1] != ')')
  56. throw conversion_error{internal::concat(
  57. "Composive value did not end in parenthesis: '", text, "'")};
  58. }
  59. /// Parse a string representation of a value of a composite type.
  60. /** @warning This version only works for UTF-8 and single-byte encodings.
  61. *
  62. * For proper encoding support, use the composite-type support in the
  63. * `field` class.
  64. */
  65. template<typename... T>
  66. inline void parse_composite(std::string_view text, T &...fields)
  67. {
  68. parse_composite(pqxx::internal::encoding_group::MONOBYTE, text, fields...);
  69. }
  70. } // namespace pqxx
  71. namespace pqxx::internal
  72. {
  73. constexpr char empty_composite_str[]{"()"};
  74. } // namespace pqxx::internal
  75. namespace pqxx
  76. {
  77. /// Estimate the buffer size needed to represent a value of a composite type.
  78. /** Returns a conservative estimate.
  79. */
  80. template<typename... T>
  81. [[nodiscard]] inline std::size_t
  82. composite_size_buffer(T const &...fields) noexcept
  83. {
  84. constexpr auto num{sizeof...(fields)};
  85. // Size for a multi-field composite includes room for...
  86. // + opening parenthesis
  87. // + field budgets
  88. // + separating comma per field
  89. // - comma after final field
  90. // + closing parenthesis
  91. // + terminating zero
  92. if constexpr (num == 0)
  93. return std::size(pqxx::internal::empty_composite_str);
  94. else
  95. return 1 + (pqxx::internal::size_composite_field_buffer(fields) + ...) +
  96. num + 1;
  97. }
  98. /// Render a series of values as a single composite SQL value.
  99. /** @warning This code is still experimental. Use with care.
  100. *
  101. * You may use this as a helper while implementing your own `string_traits`
  102. * for a composite type.
  103. */
  104. template<typename... T>
  105. inline char *composite_into_buf(char *begin, char *end, T const &...fields)
  106. {
  107. if (std::size_t(end - begin) < composite_size_buffer(fields...))
  108. throw conversion_error{
  109. "Buffer space may not be enough to represent composite value."};
  110. constexpr auto num_fields{sizeof...(fields)};
  111. if constexpr (num_fields == 0)
  112. {
  113. constexpr char empty[]{"()"};
  114. std::memcpy(begin, empty, std::size(empty));
  115. return begin + std::size(empty);
  116. }
  117. char *pos{begin};
  118. *pos++ = '(';
  119. (pqxx::internal::write_composite_field<T>(pos, end, fields), ...);
  120. // If we've got multiple fields, "backspace" that last comma.
  121. if constexpr (num_fields > 1)
  122. --pos;
  123. *pos++ = ')';
  124. *pos++ = '\0';
  125. return pos;
  126. }
  127. } // namespace pqxx
  128. #endif