relx 0.1.0
A Modern C++23 Type-Safe SQL Query Builder
Loading...
Searching...
No Matches
lazy_result.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "../query/core.hpp"
4#include "result.hpp"
5
6#include <expected>
7#include <memory>
8#include <optional>
9#include <ranges>
10#include <stdexcept>
11#include <string>
12#include <string_view>
13#include <utility>
14#include <vector>
15
16namespace relx::result {
17
19class LazyCell {
20public:
22 LazyCell(std::string_view raw_data, size_t start_pos, size_t end_pos)
23 : raw_data_(raw_data), start_pos_(start_pos), end_pos_(end_pos) {}
24
26 bool is_null() const {
27 auto value = get_raw_value();
28 return value == "NULL";
29 }
30
32 std::string get_raw_value() const {
33 return std::string(raw_data_.substr(start_pos_, end_pos_ - start_pos_));
34 }
35
37 template <typename T>
39 return as<T>(false);
40 }
41
43 template <typename T>
44 ResultProcessingResult<T> as(bool allow_numeric_bools) const {
45 // Create a temporary Cell with the parsed value and delegate
46 Cell temp_cell(get_raw_value());
47 return temp_cell.template as<T>(allow_numeric_bools);
48 }
49
50private:
51 std::string_view raw_data_;
52 size_t start_pos_;
53 size_t end_pos_;
54};
55
57class LazyRow {
58public:
60 LazyRow() : raw_data_(""), column_names_({}), cells_parsed_(false), owns_data_(false) {}
61
63 LazyRow(std::string_view raw_data, std::vector<std::string> column_names = {})
64 : raw_data_(raw_data), column_names_(std::move(column_names)), cells_parsed_(false),
65 owns_data_(false) {}
66
68 LazyRow(std::string owned_data, std::vector<std::string> column_names)
69 : raw_data_(), owned_data_(std::move(owned_data)), column_names_(std::move(column_names)),
70 cells_parsed_(false), owns_data_(true) {
71 raw_data_ = owned_data_;
72 }
73
75 LazyRow(const LazyRow& other)
76 : raw_data_(other.raw_data_), owned_data_(other.owned_data_),
77 column_names_(other.column_names_), cell_positions_(other.cell_positions_),
78 cells_parsed_(other.cells_parsed_), owns_data_(other.owns_data_) {
79 if (owns_data_) {
80 // If this row owns its data, update raw_data_ to point to our copy
81 raw_data_ = owned_data_;
82 }
83 }
84
86 LazyRow& operator=(const LazyRow& other) {
87 if (this != &other) {
88 raw_data_ = other.raw_data_;
89 owned_data_ = other.owned_data_;
90 column_names_ = other.column_names_;
91 cell_positions_ = other.cell_positions_;
92 cells_parsed_ = other.cells_parsed_;
93 owns_data_ = other.owns_data_;
94
95 if (owns_data_) {
96 // If this row owns its data, update raw_data_ to point to our copy
97 raw_data_ = owned_data_;
98 }
99 }
100 return *this;
101 }
102
104 LazyRow(LazyRow&& other) noexcept
105 : raw_data_(other.raw_data_), owned_data_(std::move(other.owned_data_)),
106 column_names_(std::move(other.column_names_)),
107 cell_positions_(std::move(other.cell_positions_)), cells_parsed_(other.cells_parsed_),
108 owns_data_(other.owns_data_) {
109 if (owns_data_) {
110 // If this row owns its data, update raw_data_ to point to our moved data
111 raw_data_ = owned_data_;
112 }
113 other.owns_data_ = false; // Other object no longer owns data
114 }
115
117 LazyRow& operator=(LazyRow&& other) noexcept {
118 if (this != &other) {
119 raw_data_ = other.raw_data_;
120 owned_data_ = std::move(other.owned_data_);
121 column_names_ = std::move(other.column_names_);
122 cell_positions_ = std::move(other.cell_positions_);
123 cells_parsed_ = other.cells_parsed_;
124 owns_data_ = other.owns_data_;
125
126 if (owns_data_) {
127 // If this row owns its data, update raw_data_ to point to our moved data
128 raw_data_ = owned_data_;
129 }
130
131 other.owns_data_ = false; // Other object no longer owns data
132 }
133 return *this;
134 }
135
138 ensure_cells_parsed();
139
140 if (index >= cell_positions_.size()) {
141 return std::unexpected(ResultError{"Cell index out of range"});
142 }
143
144 const auto& [start, end] = cell_positions_[index];
145 return LazyCell(raw_data_, start, end);
146 }
147
149 ResultProcessingResult<LazyCell> get_cell(const std::string& name) const {
150 if (column_names_.empty()) {
151 return std::unexpected(ResultError{"Column names not available"});
152 }
153
154 for (size_t i = 0; i < column_names_.size(); ++i) {
155 if (column_names_[i] == name) {
156 return get_cell(i);
157 }
158 }
159
160 return std::unexpected(ResultError{"Column name not found: " + name});
161 }
162
164 template <typename T>
166 return get<T>(index, false);
167 }
168
170 template <typename T>
171 ResultProcessingResult<T> get(size_t index, bool allow_numeric_bools) const {
172 auto cell = get_cell(index);
173 if (!cell) {
174 return std::unexpected(cell.error());
175 }
176 return cell->template as<T>(allow_numeric_bools);
177 }
178
180 template <typename T>
181 ResultProcessingResult<T> get(const std::string& name) const {
182 return get<T>(name, false);
183 }
184
186 template <typename T>
187 ResultProcessingResult<T> get(const std::string& name, bool allow_numeric_bools) const {
188 auto cell = get_cell(name);
189 if (!cell) {
190 return std::unexpected(cell.error());
191 }
192 return cell->template as<T>(allow_numeric_bools);
193 }
194
196 size_t size() const {
197 ensure_cells_parsed();
198 return cell_positions_.size();
199 }
200
202 const std::vector<std::string>& column_names() const { return column_names_; }
203
204private:
205 mutable std::string_view raw_data_;
206 std::string owned_data_; // For cases where the LazyRow owns the data
207 std::vector<std::string> column_names_;
208 mutable std::vector<std::pair<size_t, size_t>> cell_positions_;
209 mutable bool cells_parsed_;
210 bool owns_data_;
211
212 void ensure_cells_parsed() const {
213 if (cells_parsed_) return;
214
215 // Parse cell positions from raw data
216 size_t pos = 0;
217 size_t start = 0;
218
219 while (pos < raw_data_.size()) {
220 if (raw_data_[pos] == '|') {
221 cell_positions_.emplace_back(start, pos);
222 start = pos + 1;
223 }
224 ++pos;
225 }
226
227 // Add the last cell
228 if (start < raw_data_.size()) {
229 cell_positions_.emplace_back(start, raw_data_.size());
230 }
231
232 cells_parsed_ = true;
233 }
234};
235
238public:
240 LazyResultSet(std::string raw_data) : raw_data_(std::move(raw_data)), rows_parsed_(false) {}
241
243 size_t size() const {
244 ensure_rows_parsed();
245 return row_positions_.size();
246 }
247
249 bool empty() const { return size() == 0; }
250
253 ensure_rows_parsed();
254
255 if (index >= row_positions_.size()) {
256 return std::unexpected(ResultError{"Row index out of range"});
257 }
258
259 const auto& [start, end] = row_positions_[index];
260 std::string_view row_data(raw_data_.data() + start, end - start);
261 return LazyRow(row_data, column_names_);
262 }
263
266 LazyRow operator[](size_t index) const {
267 auto result = at(index);
268 if (!result) {
269 // Return empty LazyRow for invalid index
270 return LazyRow{};
271 }
272 return *result;
273 }
274
276 const std::vector<std::string>& column_names() const {
277 ensure_rows_parsed();
278 return column_names_;
279 }
280
282 class iterator {
283 public:
284 iterator(const LazyResultSet& result_set, size_t index)
285 : result_set_(result_set), index_(index) {}
286
288 auto result = result_set_.at(index_);
289 if (!result) {
290 // Return empty LazyRow for invalid index (original behavior)
291 return LazyRow{};
292 }
293 return *result;
294 }
295
297 ++index_;
298 return *this;
299 }
300
301 bool operator!=(const iterator& other) const { return index_ != other.index_; }
302
303 private:
304 const LazyResultSet& result_set_;
305 size_t index_;
306 };
307
308 iterator begin() const { return iterator(*this, 0); }
309
310 iterator end() const { return iterator(*this, size()); }
311
315 std::vector<Row> rows;
316 rows.reserve(size());
317
318 for (size_t i = 0; i < size(); ++i) {
319 auto lazy_row_result = at(i);
320 if (!lazy_row_result) {
321 return std::unexpected(lazy_row_result.error());
322 }
323
324 auto lazy_row = *lazy_row_result;
325 std::vector<Cell> cells;
326 cells.reserve(lazy_row.size());
327
328 for (size_t j = 0; j < lazy_row.size(); ++j) {
329 auto lazy_cell = lazy_row.get_cell(j);
330 if (lazy_cell) {
331 cells.emplace_back(lazy_cell->get_raw_value());
332 } else {
333 return std::unexpected(ResultError{"Failed to get cell at index " + std::to_string(j)});
334 }
335 }
336
337 rows.emplace_back(std::move(cells), column_names_);
338 }
339
340 return ResultSet(std::move(rows), column_names_);
341 }
342
343private:
344 std::string raw_data_;
345 mutable std::vector<std::pair<size_t, size_t>> row_positions_;
346 mutable std::vector<std::string> column_names_;
347 mutable bool rows_parsed_;
348
349 void ensure_rows_parsed() const {
350 if (rows_parsed_) return;
351
352 // Find line boundaries
353 size_t pos = 0;
354 size_t line_start = 0;
355 bool first_line = true;
356
357 while (pos <= raw_data_.size()) {
358 if (pos == raw_data_.size() || raw_data_[pos] == '\n') {
359 if (pos > line_start) {
360 std::string_view line(raw_data_.data() + line_start, pos - line_start);
361
362 if (first_line) {
363 // Parse header line for column names
364 parse_column_names(line);
365 first_line = false;
366 } else if (!line.empty()) {
367 // Add data row
368 row_positions_.emplace_back(line_start, pos);
369 }
370 }
371 line_start = pos + 1;
372 }
373 ++pos;
374 }
375
376 rows_parsed_ = true;
377 }
378
379 void parse_column_names(std::string_view header_line) const {
380 size_t pos = 0;
381 size_t start = 0;
382
383 while (pos < header_line.size()) {
384 if (header_line[pos] == '|') {
385 if (pos > start) {
386 column_names_.emplace_back(header_line.substr(start, pos - start));
387 }
388 start = pos + 1;
389 }
390 ++pos;
391 }
392
393 // Add the last column
394 if (start < header_line.size()) {
395 column_names_.emplace_back(header_line.substr(start));
396 }
397 }
398};
399
405template <query::SqlExpr Query>
406LazyResultSet parse_lazy(const Query& /*query*/, std::string raw_results) {
407 return LazyResultSet(std::move(raw_results));
408}
409
410} // namespace relx::result
Represents a single cell value from a database result.
Definition result.hpp:68
Lazy cell that defers parsing until accessed.
std::string get_raw_value() const
Get the raw string value (parsed on demand)
bool is_null() const
Check if the cell contains a NULL value.
ResultProcessingResult< T > as(bool allow_numeric_bools) const
Parse the cell's value as the specified type with boolean conversion options.
LazyCell(std::string_view raw_data, size_t start_pos, size_t end_pos)
Constructs a lazy cell with raw data and parsing context.
ResultProcessingResult< T > as() const
Parse the cell's value as the specified type.
iterator(const LazyResultSet &result_set, size_t index)
bool operator!=(const iterator &other) const
Lazy result set that defers row parsing until accessed.
ResultProcessingResult< LazyRow > at(size_t index) const
Get a row by index (parsed on demand)
LazyResultSet(std::string raw_data)
Constructs a lazy result set with raw data.
const std::vector< std::string > & column_names() const
Get the column names.
bool empty() const
Check if the result set is empty.
LazyRow operator[](size_t index) const
Access a row by index using the subscript operator.
size_t size() const
Get the number of rows (requires parsing row boundaries)
ResultProcessingResult< ResultSet > to_result_set() const
Transform to regular ResultSet if needed.
Lazy row that defers cell parsing until accessed.
ResultProcessingResult< T > get(size_t index) const
Get a typed value by index.
ResultProcessingResult< LazyCell > get_cell(const std::string &name) const
Get a cell by column name.
LazyRow(std::string_view raw_data, std::vector< std::string > column_names={})
Constructs a lazy row with raw data and column information.
LazyRow(std::string owned_data, std::vector< std::string > column_names)
Constructs a lazy row that owns its data (for streaming)
size_t size() const
Get the number of cells in this row.
ResultProcessingResult< T > get(const std::string &name) const
Get a typed value by column name.
ResultProcessingResult< LazyCell > get_cell(size_t index) const
Get a cell by index (parsed on demand)
LazyRow(LazyRow &&other) noexcept
Move constructor.
const std::vector< std::string > & column_names() const
Get the column names.
LazyRow()
Default constructor.
ResultProcessingResult< T > get(const std::string &name, bool allow_numeric_bools) const
Get a typed value by column name with boolean conversion options.
LazyRow & operator=(const LazyRow &other)
Copy assignment operator.
LazyRow & operator=(LazyRow &&other) noexcept
Move assignment operator.
ResultProcessingResult< T > get(size_t index, bool allow_numeric_bools) const
Get a typed value by index with boolean conversion options.
LazyRow(const LazyRow &other)
Copy constructor.
Represents the result set from a database query.
Definition result.hpp:608
Represents an index on a table.
Definition index.hpp:39
LazyResultSet parse_lazy(const Query &, std::string raw_results)
Parse raw results from a database into a lazy ResultSet.
std::expected< T, ResultError > ResultProcessingResult
Type alias for result of processing operations.
Definition result.hpp:33
STL namespace.
Error type for result processing operations.
Definition result.hpp:27