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