JAPAn
Just Another Parity Analyzer
Loading...
Searching...
No Matches
QwParitySchemaRow.h
Go to the documentation of this file.
1/*
2 * QwParitySchemaRow.h
3 *
4 * Created on: Sept 19, 2025
5 * Author: wdconinc
6 */
7
8#ifndef QWPARITYSCHEMAROW_H_
9#define QWPARITYSCHEMAROW_H_
10
11/*
12 * QwParitySchemaRow.h
13 *
14 * Template-based row structures that automatically generate from sqlpp11 schema definitions.
15 * This eliminates the need to manually duplicate field names and types while providing
16 * clean, type-safe access using the schema column definitions.
17 *
18 * Usage:
19 * QwParitySchema::beam_optics table;
20 * QwParitySchema::beam_optics_row row;
21 *
22 * // Clean syntax using schema columns
23 * row[table.analysis_id] = analysis_id_value;
24 * row[table.monitor_id] = monitor_id_value;
25 *
26 * // Or method-based access
27 * row.set(table.amplitude, amplitude_value);
28 * auto amplitude = row.get(table.amplitude);
29 *
30 * // Generate insert query automatically
31 * auto query = row.insert_query();
32 */
33
34// System headers
35#include <tuple>
36#include <type_traits>
37#include <utility>
38
39// Qweak headers
40#ifdef __USE_DATABASE__
41// Prefer the generated schema header from the build include path.
42#include <QwParitySchema.h>
43#ifdef __USE_SQLPP11__
44#include <sqlpp11/sqlpp11.h>
45#endif // __USE_SQLPP11__
46#ifdef __USE_SQLPP23__
47#include <sqlpp23/sqlpp23.h>
48#endif // __USE_SQLPP23__
49#endif // __USE_DATABASE__
50
51#ifdef __USE_DATABASE__
52
53namespace QwParitySchema {
54
55namespace detail {
56 // Helper to extract C++ value types - version specific
57#ifdef __USE_SQLPP11__
58 template<typename Column>
59 using column_value_t = sqlpp::cpp_value_type_of<typename Column::_traits::_value_type>;
60#endif // __USE_SQLPP11__
61
62#ifdef __USE_SQLPP23__
63 template<typename Column>
64 using column_value_t = sqlpp::parameter_value_t<sqlpp::data_type_of_t<Column>>;
65#endif // __USE_SQLPP23__
66
67 // Extract all column value types into a tuple
68 template<typename Tuple>
69 struct extract_value_types;
70
71 template<typename... Columns>
72 struct extract_value_types<std::tuple<Columns...>> {
73 using type = std::tuple<column_value_t<Columns>...>;
74 };
75
76 // Recursive helper to find column index - base case
77 template<typename Target>
78 constexpr std::size_t find_index_impl() {
79 static_assert(sizeof(Target) == 0, "Column type not found in table");
80 return 0; // This should never be reached due to static_assert
81 }
82
83 // Recursive helper to find column index - recursive case
84 template<typename Target, typename First, typename... Rest>
85 constexpr std::size_t find_index_impl() {
86 if constexpr (std::is_same_v<Target, First>) {
87 return 0;
88 } else {
89 return 1 + find_index_impl<Target, Rest...>();
90 }
91 }
92
93 // Helper to find the index of a specific column type in the tuple
94 template<typename TargetColumn, typename Tuple>
95 struct column_index;
96
97 template<typename TargetColumn, typename... Columns>
98 struct column_index<TargetColumn, std::tuple<Columns...>> {
99 static constexpr std::size_t value = find_index_impl<TargetColumn, Columns...>();
100 };
101
102 // Helper to check if a column/column spec is insertable (doesn't have must_not_insert trait)
103 template<typename ColumnOrColumnSpec>
104 constexpr bool is_insertable_column() {
105#ifdef __USE_SQLPP11__
106 using traits = typename ColumnOrColumnSpec::_traits;
107 using tags = typename traits::_tags;
108 return !sqlpp::detail::is_element_of<sqlpp::tag::must_not_insert, tags>::value;
109#endif // __USE_SQLPP11__
110#ifdef __USE_SQLPP23__
111 // In sqlpp23, check if column has default value (auto-increment typically has has_default)
112 return !sqlpp::has_default<ColumnOrColumnSpec>::value;
113#endif // __USE_SQLPP23__
114 }
115
116 // Proxy class for column access that prevents assignment to auto-increment fields
117 template<typename ColumnSpec, typename ValueType>
118 class column_proxy {
119 private:
120 ValueType& value_ref;
121
122 public:
123 column_proxy(ValueType& ref) : value_ref(ref) {}
124
125 // Implicit conversion to the value type for reading
126 operator const ValueType&() const {
127 return value_ref;
128 }
129
130 // Assignment operator with compile-time check
131 template<typename T>
132 column_proxy& operator=(T&& val) {
133 static_assert(is_insertable_column<ColumnSpec>(),
134 "Cannot assign to auto-increment field (has must_not_insert trait). "
135 "Auto-increment fields are generated by the database.");
136 value_ref = std::forward<T>(val);
137 return *this;
138 }
139
140 // Special assignment operator for null values - version specific
141#ifdef __USE_SQLPP11__
142 column_proxy& operator=(const sqlpp::null_t& /*null_val*/) {
143 static_assert(is_insertable_column<ColumnSpec>(),
144 "Cannot assign to auto-increment field (has must_not_insert trait). "
145 "Auto-increment fields are generated by the database.");
146
147 // Check if column can be null at compile time
148 using traits = typename ColumnSpec::_traits;
149 using tags = typename traits::_tags;
150 static_assert(sqlpp::detail::is_element_of<sqlpp::tag::can_be_null, tags>::value,
151 "Cannot assign null to non-nullable column");
152
153 // For nullable columns, set to default-constructed value (representing null)
154 value_ref = ValueType{};
155 return *this;
156 }
157#endif // __USE_SQLPP11__
158
159#ifdef __USE_SQLPP23__
160 // Helper trait to check if a type is std::optional
161 template<typename T>
162 struct is_optional_type : std::false_type {};
163 template<typename T>
164 struct is_optional_type<std::optional<T>> : std::true_type {};
165
166 column_proxy& operator=(const std::nullopt_t& /*null_val*/) {
167 static_assert(is_insertable_column<ColumnSpec>(),
168 "Cannot assign to auto-increment field (has has_default trait). "
169 "Auto-increment fields are generated by the database.");
170
171 // Check if ValueType is optional (i.e., nullable) at compile time
172 static_assert(is_optional_type<ValueType>::value,
173 "Cannot assign null to non-nullable column");
174
175 // For nullable columns, set to default-constructed value (representing null)
176 value_ref = ValueType{};
177 return *this;
178 }
179#endif // __USE_SQLPP23__
180
181 // Get the actual reference (for reading)
182 const ValueType& get() const {
183 return value_ref;
184 }
185 };
186
187 // Template helper to extract column spec from column types
188 template<typename T>
189 struct column_spec_of {
190 using type = T; // Default: assume T is already the column spec
191 };
192
193 // Both sqlpp11 and sqlpp23 use the same column_t<Table, ColumnSpec> structure
194 template<typename Table, typename ColumnSpec>
195 struct column_spec_of<sqlpp::column_t<Table, ColumnSpec>> {
196 using type = ColumnSpec;
197 };
198
199 template<typename T>
200 using column_spec_of_t = typename column_spec_of<T>::type;
201
202 // Helper to make assignment only for insertable columns
203 template<std::size_t I, typename Columns, typename Values>
204 auto make_assignment_if_insertable(const Columns& columns, const Values& values) {
205 auto column = std::get<I>(columns);
206 using column_type = std::decay_t<decltype(column)>;
207 using column_spec = column_spec_of_t<column_type>;
208 if constexpr (is_insertable_column<column_spec>()) {
209 return std::make_tuple(column = std::get<I>(values));
210 } else {
211 return std::make_tuple(); // Empty tuple for non-insertable columns
212 }
213 }
214} // namespace detail
215
216/**
217 * @brief Template-based row generator that automatically extracts column information
218 * from sqlpp11 table definitions.
219 *
220 * This class provides a type-safe, schema-synchronized way to create row structures
221 * without manually duplicating field names. It uses template metaprogramming to
222 * extract column types and provides clean access syntax using the schema column
223 * definitions.
224 *
225 * Usage Examples:
226 *
227 * Basic Usage:
228 * @code
229 * QwParitySchema::beam_optics_row row;
230 * QwParitySchema::beam_optics table;
231 *
232 * // Set values using the table column references
233 * row[table.run_number] = 12345;
234 * row[table.beam_energy] = 2.2;
235 *
236 * // Generate insert query
237 * auto query = row.insert_into();
238 * connection(query);
239 * @endcode
240 *
241 * @tparam Table The sqlpp11 table type (e.g., QwParitySchema::beam_optics)
242 */
243template<typename Table>
244class row {
245private:
246 // Extract the column tuple type from the table - both libraries use the same _column_tuple_t member
247 using column_tuple_t = typename Table::_column_tuple_t;
248 using values_tuple_t = typename detail::extract_value_types<column_tuple_t>::type;
249
250public:
251 using table_type = Table;
252
253 /**
254 * @brief Storage for all column values as a tuple
255 *
256 * The tuple contains one element for each column in the table,
257 * with types matching the sqlpp11 column value types.
258 */
259 values_tuple_t values;
260
261 /**
262 * @brief Default constructor
263 */
264 row() = default;
265
266 /**
267 * @brief Copy constructor
268 */
269 row(const row&) = default;
270
271 /**
272 * @brief Move constructor
273 */
274 row(row&&) = default;
275
276 /**
277 * @brief Copy assignment operator
278 */
279 row& operator=(const row&) = default;
280
281 /**
282 * @brief Move assignment operator
283 */
284 row& operator=(row&&) = default;
285
286 /**
287 * @brief Destructor
288 */
289 ~row() = default;
290
291 /**
292 * @brief Set a column value using the column specification type
293 *
294 * @tparam ColumnSpec The column specification type from the schema
295 * @tparam T The value type
296 * @param value The value to set
297 */
298 template<typename ColumnSpec, typename T>
299 void set(T&& value) {
300 static_assert(detail::is_insertable_column<ColumnSpec>(),
301 "Cannot set auto-increment field (has must_not_insert trait). "
302 "Auto-increment fields are generated by the database.");
303 using column_t = sqlpp::column_t<Table, ColumnSpec>;
304 constexpr auto idx = detail::column_index<column_t, column_tuple_t>::value;
305 std::get<idx>(values) = std::forward<T>(value);
306 }
307
308 /**
309 * @brief Get a column value using the column specification type
310 *
311 * @tparam ColumnSpec The column specification type from the schema
312 * @return const reference to the column value
313 */
314 template<typename ColumnSpec>
315 const auto& get() const {
316 using column_t = sqlpp::column_t<Table, ColumnSpec>;
317 constexpr auto idx = detail::column_index<column_t, column_tuple_t>::value;
318 return std::get<idx>(values);
319 }
320
321 /**
322 * @brief Get a mutable column value using the column specification type
323 *
324 * @tparam ColumnSpec The column specification type from the schema
325 * @return mutable reference to the column value
326 */
327 template<typename ColumnSpec>
328 auto& get() {
329 using column_t = sqlpp::column_t<Table, ColumnSpec>;
330 constexpr auto idx = detail::column_index<column_t, column_tuple_t>::value;
331 return std::get<idx>(values);
332 }
333
334 /**
335 * @brief Set a column value using a table column instance (auto-deducing)
336 *
337 * @tparam Column The column type (auto-deduced from parameter)
338 * @tparam T The value type
339 * @param column The table column instance
340 * @param value The value to set
341 */
342 template<typename ColumnType, typename T>
343 void set(const ColumnType& /*column*/, T&& value) {
344 using column_spec = detail::column_spec_of_t<ColumnType>;
345 static_assert(detail::is_insertable_column<column_spec>(),
346 "Cannot set auto-increment field. "
347 "Auto-increment fields are generated by the database.");
348 set<column_spec>(std::forward<T>(value));
349 }
350
351 /**
352 * @brief Get a column value using a table column instance (auto-deducing)
353 *
354 * @tparam Column The column type (auto-deduced from parameter)
355 * @param column The table column instance
356 * @return const reference to the column value
357 */
358 template<typename ColumnType>
359 const auto& get(const ColumnType& /*column*/) const {
360 using column_spec = detail::column_spec_of_t<ColumnType>;
361 return get<column_spec>();
362 }
363
364 /**
365 * @brief Get a mutable column value using a table column instance (auto-deducing)
366 *
367 * @tparam Column The column type (auto-deduced from parameter)
368 * @param column The table column instance
369 * @return mutable reference to the column value
370 */
371 template<typename ColumnType>
372 auto& get(const ColumnType& /*column*/) {
373 using column_spec = detail::column_spec_of_t<ColumnType>;
374 return get<column_spec>();
375 }
376
377 /**
378 * @brief Array-style access operator for setting/getting column values
379 *
380 * Returns a proxy object that allows reading and provides compile-time
381 * protection against assignment to auto-increment fields.
382 *
383 * @tparam Column The column type (auto-deduced from parameter)
384 * @param column The table column instance
385 * @return proxy object for safe column access
386 */
387 template<typename ColumnType>
388 auto operator[](const ColumnType& /*column*/) {
389 using column_spec = detail::column_spec_of_t<ColumnType>;
390 constexpr auto idx = detail::column_index<ColumnType, column_tuple_t>::value;
391 using value_type = std::tuple_element_t<idx, values_tuple_t>;
392 return detail::column_proxy<column_spec, value_type>(std::get<idx>(values));
393 }
394
395 /**
396 * @brief Const array-style access operator for getting column values
397 *
398 * @tparam Column The column type (auto-deduced from parameter)
399 * @param column The table column instance
400 * @return const reference to the column value
401 */
402 template<typename ColumnType>
403 const auto& operator[](const ColumnType& column) const {
404 return get(column);
405 }
406
407 /**
408 * @brief Generate an sqlpp11 insert query from the row data
409 *
410 * This method automatically maps all row values to their corresponding
411 * table columns and creates a properly typed insert statement.
412 *
413 * @return sqlpp11 insert query object
414 */
415 auto insert_into() const {
416 Table table;
417 return generate_insert_impl(table, std::make_index_sequence<std::tuple_size_v<values_tuple_t>>{});
418 }
419
420 /**
421 * @brief Reset all column values to their default-constructed state
422 */
423 void reset() {
424 values = values_tuple_t{};
425 }
426
427 /**
428 * @brief Get the number of columns in this row
429 *
430 * @return number of columns
431 */
432 static constexpr std::size_t column_count() {
433 return std::tuple_size_v<values_tuple_t>;
434 }
435
436private:
437 /**
438 * @brief Implementation helper for generating insert queries
439 *
440 * Uses index sequences to map tuple elements to table columns,
441 * but skips columns with must_not_insert trait (like auto-increment fields).
442 *
443 * @tparam Is Index sequence for the tuple elements
444 * @param table The table instance
445 * @param Index sequence (unused parameter)
446 * @return sqlpp11 insert query
447 */
448 template<std::size_t... Is>
449 auto generate_insert_impl(Table& table, std::index_sequence<Is...>) const {
450 auto columns = sqlpp::all_of(table);
451
452 // Create a tuple of all assignments (including empty tuples for non-insertable columns)
453 auto assignments = std::tuple_cat(
454 detail::make_assignment_if_insertable<Is>(columns, values)...
455 );
456
457 // Apply the assignments to the insert query using tuple unpacking
458 return std::apply([&table](auto&&... args) {
459 return sqlpp::insert_into(table).set(args...);
460 }, assignments);
461 }
462};
463
464// Convenience type aliases for common tables
465using beam_optics_row = row<beam_optics>;
466using md_data_row = row<md_data>;
467using lumi_data_row = row<lumi_data>;
468using beam_row = row<beam>;
469using beam_errors_row = row<beam_errors>;
470using lumi_errors_row = row<lumi_errors>;
471using md_errors_row = row<md_errors>;
472using general_errors_row = row<general_errors>;
473
474} // namespace QwParitySchema
475
476#endif // __USE_DATABASE__
477
478#endif // QWPARITYSCHEMAROW_H_