relx 0.1.0
A Modern C++23 Type-Safe SQL Query Builder
Loading...
Searching...
No Matches
result.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "../query/core.hpp"
4#include "../query/meta.hpp"
5#include "../schema/core.hpp"
6#include "../schema/table.hpp"
7
8#include <array>
9#include <concepts>
10#include <expected>
11#include <functional>
12#include <iostream>
13#include <optional>
14#include <ranges>
15#include <sstream>
16#include <stdexcept>
17#include <string>
18#include <string_view>
19#include <tuple>
20#include <type_traits>
21#include <utility>
22#include <vector>
23
24namespace relx::result {
25
28 std::string message;
29};
30
32template <typename T>
33using ResultProcessingResult = std::expected<T, ResultError>;
34
36template <typename T, typename C>
37static constexpr auto class_of(T C::*) {
38 return (C*)nullptr;
39}
40
42template <auto MemberPtr>
43constexpr std::string_view get_column_name() {
44 using Class = query::class_of_t_t<decltype(MemberPtr)>;
45 using ColumnType = std::remove_reference_t<decltype(std::declval<Class>().*MemberPtr)>;
46 return ColumnType::column_name;
47}
48
50template <typename Table, typename ColumnMemberPtr>
53 std::remove_reference_t<decltype(std::declval<Table>().*std::declval<ColumnMemberPtr>())>;
54 using type = typename column_type::value_type;
55};
56
57template <typename Table, typename ColumnMemberPtr>
59
61template <typename Table, typename ColumnMemberPtr>
62std::string get_column_name_from_ptr(ColumnMemberPtr ptr) {
63 using ColumnType = std::remove_reference_t<decltype(std::declval<Table>().*ptr)>;
64 return std::string(ColumnType::name);
65}
66
68class Cell {
69public:
71 explicit Cell(std::string value) : value_(std::move(value)) {}
72
74 bool is_null() const { return value_ == "NULL"; }
75
77 const std::string& raw_value() const { return value_; }
78
82 template <typename T>
84 return as<T>(false);
85 }
86
92 template <typename T>
93 ResultProcessingResult<T> as(bool allow_numeric_bools) const {
94 if (is_null()) {
95 if constexpr (is_optional_v<T>) {
96 return T{std::nullopt};
97 }
98 return std::unexpected(ResultError{"Cannot convert NULL to non-optional type"});
99 }
100
101 // More strict type checking
102 if constexpr (std::is_same_v<T, bool>) {
103 const auto lower = to_lower(value_);
104
105 // Always accept explicit boolean strings
106 if (lower == "true") {
107 return true;
108 }
109 if (lower == "false") {
110 return false;
111 }
112 if (lower == "t") { // PostgreSQL format
113 return true;
114 }
115 if (lower == "f") { // PostgreSQL format
116 return false;
117 }
118 if (allow_numeric_bools) {
119 // Only allow numeric conversion if explicitly allowed
120 if (lower == "1") {
121 return true;
122 }
123 if (lower == "0") {
124 return false;
125 }
126 }
127
128 return std::unexpected(ResultError{std::string("Cannot convert '") + value_ +
129 "' to boolean: not a boolean value"});
130 } else if constexpr (std::is_same_v<T, int> || std::is_same_v<T, long> ||
131 std::is_same_v<T, long long>) {
132 // For integer types, reject any attempt to convert from boolean strings
133 const auto lower = to_lower(value_);
134 if (lower == "true" || lower == "false") {
135 return std::unexpected(ResultError{"Cannot convert boolean value to integer type"});
136 }
137
138 // Use the appropriate parsing for the integer type
139 try {
140 if (!is_valid_integer(value_)) {
141 return std::unexpected(ResultError{"Cannot convert to integer: invalid format"});
142 }
143
144 if constexpr (std::is_same_v<T, int>) {
145 return std::stoi(value_);
146 } else if constexpr (std::is_same_v<T, long>) {
147 return std::stol(value_);
148 } else if constexpr (std::is_same_v<T, long long>) {
149 return std::stoll(value_);
150 }
151 } catch (const std::exception& e) {
152 return std::unexpected(ResultError{std::string("Error parsing cell value '") + value_ +
153 "' to integer: " + e.what()});
154 }
155 } else if constexpr (schema::ColumnTypeConcept<T>) {
156 try {
158 } catch (const std::exception& e) {
159 return std::unexpected(ResultError{std::string("Error parsing cell value '") + value_ +
160 "' to column type: " + e.what()});
161 }
162 } else {
163 // Use appropriate parsing based on the target type
164 return parse_value<T>(allow_numeric_bools);
165 }
166 }
167
168private:
169 std::string value_;
170
171 // Helper to detect optional types
172 template <typename T>
173 static constexpr bool is_optional_v = false;
174
175 // String helpers
176 static std::string to_lower(std::string str) {
177 std::transform(str.begin(), str.end(), str.begin(),
178 [](unsigned char c) { return std::tolower(c); });
179 return str;
180 }
181
182 // Check if a value is likely a boolean
183 static bool is_boolean_value(const std::string& str) {
184 // Only accept "true", "false", "0", or "1" as possible boolean values
185 return str == "true" || str == "false" || str == "0" || str == "1";
186 }
187
188 // Type-specific parsing implementations
189 template <typename T>
190 ResultProcessingResult<T> parse_value(bool allow_numeric_bools) const {
191 try {
192 if constexpr (std::is_same_v<T, int>) {
193 // Validate the string contains only digits and optional sign
194 if (!is_valid_integer(value_)) {
195 return std::unexpected(ResultError{"Cannot convert to int: invalid format"});
196 }
197 return std::stoi(value_);
198 } else if constexpr (std::is_same_v<T, long>) {
199 if (!is_valid_integer(value_)) {
200 return std::unexpected(ResultError{"Cannot convert to long: invalid format"});
201 }
202 return std::stol(value_);
203 } else if constexpr (std::is_same_v<T, long long>) {
204 if (!is_valid_integer(value_)) {
205 return std::unexpected(ResultError{"Cannot convert to long long: invalid format"});
206 }
207 return std::stoll(value_);
208 } else if constexpr (std::is_same_v<T, unsigned long>) {
209 if (!is_valid_unsigned_integer(value_)) {
210 return std::unexpected(ResultError{"Cannot convert to unsigned long: invalid format"});
211 }
212 return std::stoul(value_);
213 } else if constexpr (std::is_same_v<T, unsigned long long>) {
214 if (!is_valid_unsigned_integer(value_)) {
215 return std::unexpected(
216 ResultError{"Cannot convert to unsigned long long: invalid format"});
217 }
218 return std::stoull(value_);
219 } else if constexpr (std::is_same_v<T, float>) {
220 if (!is_valid_float(value_)) {
221 return std::unexpected(ResultError{"Cannot convert to float: invalid format"});
222 }
223 return std::stof(value_);
224 } else if constexpr (std::is_same_v<T, double>) {
225 if (!is_valid_float(value_)) {
226 return std::unexpected(ResultError{"Cannot convert to double: invalid format"});
227 }
228 return std::stod(value_);
229 } else if constexpr (std::is_same_v<T, long double>) {
230 if (!is_valid_float(value_)) {
231 return std::unexpected(ResultError{"Cannot convert to long double: invalid format"});
232 }
233 return std::stold(value_);
234 } else if constexpr (std::is_same_v<T, std::string>) {
235 return value_;
236 } else if constexpr (is_optional_v<T>) {
237 using ValueType = typename T::value_type;
238 auto result = as<ValueType>(allow_numeric_bools);
239 if (result) {
240 return T{*result};
241 }
242 return T{std::nullopt};
243 } else {
244 // For any other type, throw a clear conversion error
245 return std::unexpected(
246 ResultError{std::string("Unsupported type conversion from '") + value_ + "'"});
247 }
248 } catch (const std::exception& e) {
249 return std::unexpected(
250 ResultError{std::string("Error parsing cell value '") + value_ + "': " + e.what()});
251 }
252 }
253
254 // Helper functions for validation
255 static bool is_valid_integer(const std::string& str) {
256 if (str.empty()) {
257 return false;
258 }
259
260 size_t start = 0;
261 if (str[0] == '-' || str[0] == '+') {
262 start = 1;
263 }
264
265 return str.length() > start && std::all_of(str.begin() + static_cast<std::ptrdiff_t>(start),
266 str.end(), [](char c) { return std::isdigit(c); });
267 }
268
269 static bool is_valid_unsigned_integer(const std::string& str) {
270 if (str.empty()) {
271 return false;
272 }
273
274 size_t start = 0;
275 if (str[0] == '+') {
276 start = 1;
277 }
278
279 return str.length() > start && std::all_of(str.begin() + static_cast<std::ptrdiff_t>(start),
280 str.end(), [](char c) { return std::isdigit(c); });
281 }
282
283 static bool is_valid_float(const std::string& str) {
284 if (str.empty()) {
285 return false;
286 }
287
288 bool has_digit = false;
289 bool has_decimal = false;
290 bool has_exponent = false;
291
292 size_t i = 0;
293 if (str[0] == '-' || str[0] == '+') {
294 i++;
295 }
296
297 for (; i < str.length(); i++) {
298 const char c = str[i];
299
300 if (std::isdigit(c)) {
301 has_digit = true;
302 } else if (c == '.') {
303 if (has_decimal || has_exponent) {
304 return false;
305 }
306 has_decimal = true;
307 } else if (c == 'e' || c == 'E') {
308 if (!has_digit || has_exponent) {
309 return false;
310 }
311 has_exponent = true;
312
313 if (i + 1 < str.length() && (str[i + 1] == '+' || str[i + 1] == '-')) {
314 i++;
315 }
316 if (i + 1 >= str.length()) {
317 return false; // No digits after exponent
318 }
319 } else {
320 return false;
321 }
322 }
323
324 return has_digit;
325 }
326
327 // Specialization for optional types
328 template <typename T>
329 ResultProcessingResult<std::optional<T>> parse_value(bool allow_numeric_bools) const
330 requires requires { parse_value<T>(allow_numeric_bools); }
331 {
332 if (is_null()) {
333 return std::optional<T>{std::nullopt};
334 }
335
336 auto result = parse_value<T>(allow_numeric_bools);
337 if (!result) {
338 return std::unexpected(result.error());
339 }
340 return std::optional<T>{*result};
341 }
342};
343
344// Specialization for Cell::is_optional_v
345template <typename T>
346constexpr bool Cell::is_optional_v<std::optional<T>> = true;
347
349class Row {
350public:
354 explicit Row(std::vector<Cell> cells, std::vector<std::string> column_names = {})
355 : cells_(std::move(cells)), column_names_(std::move(column_names)) {}
356
361 if (index >= cells_.size()) {
362 return std::unexpected(ResultError{"Cell index out of range"});
363 }
364 return &cells_[index];
365 }
366
370 ResultProcessingResult<const Cell*> get_cell(const std::string& name) const {
371 if (column_names_.empty()) {
372 return std::unexpected(ResultError{"Column names not available"});
373 }
374
375 for (size_t i = 0; i < column_names_.size(); ++i) {
376 if (column_names_[i] == name && i < cells_.size()) {
377 return &cells_[i];
378 }
379 if (column_names_[i] == name) {
380 // Found the column name but missing the corresponding cell
381 return std::unexpected(ResultError{"Column found but missing cell data: " + name});
382 }
383 }
384
385 return std::unexpected(ResultError{"Column name not found: " + name});
386 }
387
392 template <typename T>
394 return get<T>(index, false);
395 }
396
402 template <typename T>
403 ResultProcessingResult<T> get(size_t index, bool allow_numeric_bools) const {
404 auto cell = get_cell(index);
405 if (!cell) {
406 return std::unexpected(cell.error());
407 }
408 return (*cell)->template as<T>(allow_numeric_bools);
409 }
410
415 template <typename T>
416 ResultProcessingResult<T> get(const std::string& name) const {
417 return get<T>(name, false);
418 }
419
425 template <typename T>
426 ResultProcessingResult<T> get(const std::string& name, bool allow_numeric_bools) const {
427 auto cell = get_cell(name);
428 if (!cell) {
429 return std::unexpected(cell.error());
430 }
431 return (*cell)->template as<T>(allow_numeric_bools);
432 }
433
439 template <typename T, typename ColType>
440 ResultProcessingResult<T> get(const ColType& column) const {
441 return get<T>(column, false);
442 }
443
450 template <typename T, typename ColType>
451 ResultProcessingResult<T> get(const ColType& column, bool allow_numeric_bools) const {
452 if constexpr (requires {
453 { column.column_name } -> std::convertible_to<std::string_view>;
454 }) {
455 // If we have a column object with a column_name, use that
456 return get<T>(std::string(column.column_name), allow_numeric_bools);
457 } else if constexpr (requires {
458 { column.name } -> std::convertible_to<std::string_view>;
459 }) {
460 // Some column-like objects might use 'name' instead
461 return get<T>(std::string(column.name), allow_numeric_bools);
462 } else if constexpr (std::is_same_v<ColType, std::string> ||
463 std::is_same_v<ColType, std::string_view> ||
464 std::is_convertible_v<ColType, std::string_view>) {
465 // If we have a string, string_view, or convertible to string_view (like char array), use that
466 // directly
467 return get<T>(std::string(column), allow_numeric_bools);
468 } else if constexpr (std::is_convertible_v<ColType, size_t>) {
469 // If we can convert to size_t, use as an index
470 return get<T>(static_cast<size_t>(column), allow_numeric_bools);
471 } else {
472 // For other custom column types
473 static_assert(
474 requires {
475 { column } -> std::convertible_to<std::string_view>;
476 } ||
477 requires {
478 { column } -> std::convertible_to<size_t>;
479 } ||
480 requires {
481 { column.name } -> std::convertible_to<std::string_view>;
482 } ||
483 requires {
484 { column.column_name } -> std::convertible_to<std::string_view>;
485 },
486 "Column must be convertible to a string, size_t, or have a name/column_name member");
487
488 // This should never be reached due to the static_assert above
489 return std::unexpected(ResultError{"Invalid column type"});
490 }
491 }
492
496 template <auto MemberPtr>
497 auto get() const {
498 using Class = query::class_of_t_t<decltype(MemberPtr)>;
499 using ColumnType = std::remove_reference_t<decltype(std::declval<Class>().*MemberPtr)>;
500 using ValueType = typename ColumnType::value_type;
501
502 // Find the column name
503 static constexpr auto column_name = ColumnType::name;
504
505 // Delegate to the string-based version
506 return get<ValueType>(std::string(column_name));
507 }
508
512 template <auto MemberPtr>
513 auto get_optional() const {
514 using Class = query::class_of_t_t<decltype(MemberPtr)>;
515 using ColumnType = std::remove_reference_t<decltype(std::declval<Class>().*MemberPtr)>;
516 using ValueType = typename ColumnType::value_type;
517
518 // Find the column name
519 static constexpr auto column_name = ColumnType::name;
520
521 // Delegate to the string-based version
522 return get<std::optional<ValueType>>(std::string(column_name));
523 }
524
527 size_t size() const { return cells_.size(); }
528
531 const std::vector<std::string>& column_names() const { return column_names_; }
532
533 std::string to_string() const {
534 std::stringstream result;
535 result << "| ";
536 for (const auto& cell : cells_) {
537 result << cell.raw_value() << " | ";
538 }
539 return result.str();
540 }
541
542private:
543 std::vector<Cell> cells_;
544 std::vector<std::string> column_names_;
545};
546
548template <typename... Types>
550 // Store the actual tuple of values
551 std::tuple<Types...> values;
552
553 // Allow direct access to elements for destructuring
554 template <size_t I>
555 friend const auto& get(const RowAdapter& a) {
556 return std::get<I>(a.values);
557 }
558};
559
561template <typename ResultSet, typename... Types>
564 size_t index;
565 std::array<size_t, sizeof...(Types)> column_indices;
566
567 bool operator!=(const RowIterator& other) const { return index != other.index; }
568
570 ++index;
571 return *this;
572 }
573
574 auto operator*() const { return get_row_values(std::make_index_sequence<sizeof...(Types)>{}); }
575
576private:
577 template <size_t... Indices>
578 RowAdapter<Types...> get_row_values(std::index_sequence<Indices...>) const {
579 const auto& row = results.at(index);
580
581 return RowAdapter<Types...>{
582 std::tuple<Types...>{[&row, this]() -> std::tuple_element_t<Indices, std::tuple<Types...>> {
583 using ResultType = std::tuple_element_t<Indices, std::tuple<Types...>>;
584 // Allow numeric boolean conversion for structured binding
585 bool allow_numeric_bools = std::is_same_v<ResultType, bool>;
586 auto result = row.template get<ResultType>(column_indices[Indices], allow_numeric_bools);
587 if (result) {
588 return *result;
589 }
590 // Return default value for the type if conversion fails
591 return ResultType{};
592 }()...}};
593 }
594};
595
597template <typename ResultSet, typename... Types>
598struct RowsView {
600 std::array<size_t, sizeof...(Types)> column_indices;
601
602 RowIterator<ResultSet, Types...> begin() const { return {results, 0, column_indices}; }
603
604 RowIterator<ResultSet, Types...> end() const { return {results, results.size(), column_indices}; }
605};
606
609public:
611 ResultSet() = default;
612
614 ResultSet(std::vector<Row> rows, std::vector<std::string> column_names = {})
615 : rows_(std::move(rows)), column_names_(std::move(column_names)) {}
616
619 size_t size() const { return rows_.size(); }
620
623 bool empty() const { return rows_.empty(); }
624
628 const Row& at(size_t index) const { return rows_.at(index); }
629
633 const Row& operator[](size_t index) const { return rows_.at(index); }
634
637 size_t column_count() const { return column_names_.size(); }
638
643 if (index >= column_names_.size()) {
644 return std::unexpected(ResultError{"Column index out of range"});
645 }
646 return column_names_.at(index);
647 }
648
651 const std::vector<std::string>& column_names() const { return column_names_; }
652
655 auto begin() const { return rows_.begin(); }
656
659 auto end() const { return rows_.end(); }
660
665 template <typename T>
666 std::vector<T> transform(std::function<ResultProcessingResult<T>(const Row&)> mapper) const {
667 std::vector<T> result;
668 result.reserve(rows_.size());
669
670 for (const auto& row : rows_) {
671 auto transformed = mapper(row);
672 if (transformed) {
673 result.push_back(std::move(*transformed));
674 }
675 }
676
677 return result;
678 }
679
683 template <typename... Types>
684 auto as() const {
685 // Default to first N columns where N is the number of Types
686 std::array<size_t, sizeof...(Types)> indices;
687 for (size_t i = 0; i < sizeof...(Types); ++i) {
688 indices[i] = i;
689 }
690 return as<Types...>(indices);
691 }
692
697 template <typename... Types>
698 auto as(const std::array<size_t, sizeof...(Types)>& indices) const {
699 return RowsView<ResultSet, Types...>{*this, indices};
700 }
701
706 template <typename... Types>
707 auto as(const std::array<std::string, sizeof...(Types)>& column_names) const {
708 std::array<size_t, sizeof...(Types)> indices;
709
710 for (size_t i = 0; i < sizeof...(Types); ++i) {
711 bool found = false;
712 for (size_t col_idx = 0; col_idx < column_names_.size(); ++col_idx) {
713 if (column_names_[col_idx] == column_names[i]) {
714 indices[i] = col_idx;
715 found = true;
716 break;
717 }
718 }
719 if (!found) {
720 // If column name not found, default to index or 0
721 indices[i] = (i < column_names_.size()) ? i : 0;
722 }
723 }
724
725 return as<Types...>(indices);
726 }
727
748 template <typename Table, typename P1, typename... Ps>
750 auto with_schema(P1 mp1, Ps... mps) const {
751 constexpr size_t num_cols = 1 + sizeof...(Ps);
752 std::array<std::string, num_cols> column_names;
753
754 // Fill the column names array using our helper
755 column_names[0] = get_column_name_from_ptr<Table>(mp1);
756 if constexpr (sizeof...(Ps) > 0) {
757 size_t i = 1;
758 ((column_names[i++] = get_column_name_from_ptr<Table>(mps)), ...);
759 }
760
761 // Call the as<> method with the right types
762 return as<column_member_value_t<Table, P1>, column_member_value_t<Table, Ps>...>(column_names);
763 }
764
786 template <typename Table, typename P1, typename... Ps>
788 auto with_schema(const Table& /*table*/, P1 mp1, Ps... mps) const {
789 return with_schema<std::remove_reference_t<Table>>(mp1, mps...);
790 }
791
792 std::string to_string() const {
793 std::string result;
794 for (const auto& row : rows_) {
795 result += row.to_string();
796 std::cout << '\n';
797 }
798 return result;
799 }
800
801private:
802 std::vector<Row> rows_;
803 std::vector<std::string> column_names_;
804};
805
811template <query::SqlExpr Query>
812ResultProcessingResult<ResultSet> parse(const Query& /*query*/, const std::string& raw_results) {
813 try {
814 // Split the raw results into lines
815 std::vector<std::string> lines;
816 size_t pos = 0;
817 size_t next_pos = 0;
818
819 // Parse header line and data lines
820 while ((next_pos = raw_results.find('\n', pos)) != std::string::npos) {
821 lines.push_back(raw_results.substr(pos, next_pos - pos));
822 pos = next_pos + 1;
823 }
824
825 // Add the last line if it's not empty
826 if (pos < raw_results.size()) {
827 lines.push_back(raw_results.substr(pos));
828 }
829
830 if (lines.empty()) {
831 return ResultSet{};
832 }
833
834 // Parse column names from the first line
835 std::vector<std::string> column_names;
836 const std::string header = lines[0];
837 pos = 0;
838
839 while ((next_pos = header.find('|', pos)) != std::string::npos) {
840 column_names.push_back(header.substr(pos, next_pos - pos));
841 pos = next_pos + 1;
842 }
843
844 // Add the last column
845 if (pos < header.size()) {
846 column_names.push_back(header.substr(pos));
847 }
848
849 // Parse data rows
850 std::vector<Row> rows;
851
852 for (size_t i = 1; i < lines.size(); ++i) {
853 const auto& line = lines[i];
854
855 // Skip empty lines
856 if (line.empty()) {
857 continue;
858 }
859
860 // Parse cells
861 std::vector<Cell> cells;
862 pos = 0;
863
864 while ((next_pos = line.find('|', pos)) != std::string::npos) {
865 cells.emplace_back(line.substr(pos, next_pos - pos));
866 pos = next_pos + 1;
867 }
868
869 // Add the last cell
870 if (pos < line.size()) {
871 cells.emplace_back(line.substr(pos));
872 }
873
874 // Create a row with the cells and column names
875 rows.emplace_back(std::move(cells), column_names);
876 }
877
878 return ResultSet{std::move(rows), std::move(column_names)};
879 } catch (const std::exception& e) {
880 return std::unexpected(ResultError{std::string("Error parsing results: ") + e.what()});
881 }
882}
883
884} // namespace relx::result
885
886// Structured binding support
887namespace std {
888template <typename... Types>
889struct tuple_size<relx::result::RowAdapter<Types...>>
890 : std::integral_constant<size_t, sizeof...(Types)> {};
891
892template <size_t I, typename... Types>
893struct tuple_element<I, relx::result::RowAdapter<Types...>> {
894 using type = std::tuple_element_t<I, std::tuple<Types...>>;
895};
896} // namespace std
897
898// Include lazy and streaming functionality
899#include "lazy_result.hpp"
900#include "streaming_result.hpp"
Represents a single cell value from a database result.
Definition result.hpp:68
bool is_null() const
Check if the cell contains a NULL value.
Definition result.hpp:74
ResultProcessingResult< T > as() const
Parse the cell's value as the specified type.
Definition result.hpp:83
ResultProcessingResult< T > as(bool allow_numeric_bools) const
Parse the cell's value as the specified type with an option to allow numeric boolean conversion.
Definition result.hpp:93
Cell(std::string value)
Constructs a cell with a raw string value from the database.
Definition result.hpp:71
const std::string & raw_value() const
Get the raw string value.
Definition result.hpp:77
Represents the result set from a database query.
Definition result.hpp:608
auto begin() const
Get an iterator to the beginning of the rows.
Definition result.hpp:655
auto with_schema(const Table &, P1 mp1, Ps... mps) const
Creates a structured binding view using table schema information.
Definition result.hpp:788
const Row & at(size_t index) const
Get a row by index.
Definition result.hpp:628
auto as(const std::array< size_t, sizeof...(Types)> &indices) const
Helper for structured binding with explicit types and column indices.
Definition result.hpp:698
std::string to_string() const
Definition result.hpp:792
auto as(const std::array< std::string, sizeof...(Types)> &column_names) const
Helper for structured binding with explicit types and column names.
Definition result.hpp:707
bool empty() const
Check if the result set is empty.
Definition result.hpp:623
const Row & operator[](size_t index) const
Access a row by index using the subscript operator.
Definition result.hpp:633
size_t size() const
Get the number of rows in the result set.
Definition result.hpp:619
auto as() const
Helper for structured binding with explicit types.
Definition result.hpp:684
ResultSet(std::vector< Row > rows, std::vector< std::string > column_names={})
Constructs a result set with rows and column names.
Definition result.hpp:614
ResultProcessingResult< std::string > column_name(size_t index) const
Get the name of a column by index.
Definition result.hpp:642
ResultSet()=default
Default constructor.
const std::vector< std::string > & column_names() const
Get the column names.
Definition result.hpp:651
size_t column_count() const
Get the number of columns in the result set.
Definition result.hpp:637
auto with_schema(P1 mp1, Ps... mps) const
Creates a structured binding view using table schema information.
Definition result.hpp:750
auto end() const
Get an iterator to the end of the rows.
Definition result.hpp:659
std::vector< T > transform(std::function< ResultProcessingResult< T >(const Row &)> mapper) const
Transform each row into an object of type T.
Definition result.hpp:666
Represents a single row from a database result.
Definition result.hpp:349
ResultProcessingResult< const Cell * > get_cell(const std::string &name) const
Get a cell by column name.
Definition result.hpp:370
ResultProcessingResult< T > get(size_t index) const
Get a typed value by index.
Definition result.hpp:393
ResultProcessingResult< T > get(const ColType &column, bool allow_numeric_bools) const
Get a typed value using a schema column object with allow_numeric_bools.
Definition result.hpp:451
auto get_optional() const
Get an optional typed value using a member pointer.
Definition result.hpp:513
const std::vector< std::string > & column_names() const
Get the column names.
Definition result.hpp:531
ResultProcessingResult< T > get(size_t index, bool allow_numeric_bools) const
Get a typed value by index with allow_numeric_bools.
Definition result.hpp:403
ResultProcessingResult< T > get(const ColType &column) const
Get a typed value using a schema column object.
Definition result.hpp:440
Row(std::vector< Cell > cells, std::vector< std::string > column_names={})
Constructs a row with cells and column names.
Definition result.hpp:354
ResultProcessingResult< T > get(const std::string &name) const
Get a typed value by column name.
Definition result.hpp:416
std::string to_string() const
Definition result.hpp:533
auto get() const
Get a typed value using a member pointer.
Definition result.hpp:497
ResultProcessingResult< const Cell * > get_cell(size_t index) const
Get a cell by index.
Definition result.hpp:360
ResultProcessingResult< T > get(const std::string &name, bool allow_numeric_bools) const
Get a typed value by column name with allow_numeric_bools.
Definition result.hpp:426
size_t size() const
Get the number of cells in this row.
Definition result.hpp:527
Represents a column in a database table.
Definition column.hpp:217
static constexpr auto name
The column name.
Definition column.hpp:226
Represents an index on a table.
Definition index.hpp:39
Concept for a database table type.
Definition table.hpp:31
typename class_of_t< T >::type class_of_t_t
Definition meta.hpp:66
static constexpr auto class_of(T C::*)
Helper to get the class type from a member pointer.
Definition result.hpp:37
std::string get_column_name_from_ptr(ColumnMemberPtr ptr)
Get column name from a member pointer.
Definition result.hpp:62
typename column_member_value< Table, ColumnMemberPtr >::type column_member_value_t
Definition result.hpp:58
ResultProcessingResult< ResultSet > parse(const Query &, const std::string &raw_results)
Parse raw results from a database into a typed ResultSet (eager parsing)
Definition result.hpp:812
std::expected< T, ResultError > ResultProcessingResult
Type alias for result of processing operations.
Definition result.hpp:33
constexpr std::string_view get_column_name()
Gets the column name from a column member pointer.
Definition result.hpp:43
relx database connection
STL namespace.
Error type for result processing operations.
Definition result.hpp:27
Class to support structured binding for ResultSet.
Definition result.hpp:549
std::tuple< Types... > values
Definition result.hpp:551
friend const auto & get(const RowAdapter &a)
Definition result.hpp:555
Iterator that yields RowAdapters.
Definition result.hpp:562
const ResultSet & results
Definition result.hpp:563
std::array< size_t, sizeof...(Types)> column_indices
Definition result.hpp:565
RowIterator & operator++()
Definition result.hpp:569
bool operator!=(const RowIterator &other) const
Definition result.hpp:567
View class for structured binding support.
Definition result.hpp:598
RowIterator< ResultSet, Types... > begin() const
Definition result.hpp:602
RowIterator< ResultSet, Types... > end() const
Definition result.hpp:604
std::array< size_t, sizeof...(Types)> column_indices
Definition result.hpp:600
const ResultSet & results
Definition result.hpp:599
Helper to get the value type from a column member pointer.
Definition result.hpp:51
typename column_type::value_type type
Definition result.hpp:54
std::remove_reference_t< decltype(std::declval< Table >().*std::declval< ColumnMemberPtr >())> column_type
Definition result.hpp:53
static T from_sql_string(const std::string &value)
Parse a SQL string representation to a C++ value.