2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
9// Utility function to compare SQLite type to C++ type
10template <ValidColumnType T>
11[[maybe_unused]] bool checkType(int sqliteType) {
12 if constexpr (std::is_floating_point_v<T>) {
13 return sqliteType == SQLITE_FLOAT || sqliteType == SQLITE_NULL;
14 } else if constexpr (std::is_integral_v<T>) {
15 return sqliteType == SQLITE_INTEGER || sqliteType == SQLITE_NULL;
16 } else if constexpr (std::is_same_v<T, std::string>) {
17 return sqliteType == SQLITE_TEXT || sqliteType == SQLITE_BLOB ||
18 sqliteType == SQLITE_NULL;
26 requires std::integral<I>
27void Statement::bind(int index, I value) {
28 bind(index, std::int64_t(value));
31template <ValidColumnType T, std::size_t I>
32T Statement::column() {
33 if constexpr (std::is_floating_point_v<T>) {
34 return sqlite3_column_double(m_stmt, I);
35 } else if constexpr (std::is_integral_v<T>) {
36 return sqlite3_column_int64(m_stmt, I);
37 } else if constexpr (std::is_same_v<T, std::string>) {
38 return std::string((char*)sqlite3_column_text(m_stmt, I));
41 "Unsupported result type for column. Supported types are "
42 "std::int64_t, double, and std::string");
46template <ValidColumnType... Ts, std::size_t... Is>
47std::tuple<Ts...> Statement::columns(std::index_sequence<Is...>) {
48 return std::make_tuple<Ts...>(column<Ts, Is>()...);
51template <ValidColumnType... ReturnArgs, ValidParamType... ParamArgs>
52ResultTypeWrapper<ReturnArgs...>::type Statement::run(ParamArgs... params) {
54 throw std::logic_error("Can't run unfilled Statement!");
56 // Lock mutex for thread safety
57 std::lock_guard lck{m_stmtMutex};
62 if (sqlite3_bind_parameter_count(m_stmt) != sizeof...(params)) {
63 throw std::logic_error(
64 std::format("Parameter count mismatch in SQLite statement execution: "
65 "got {} but need {}\n"
66 "Statement defined at {} [{}:{}]",
67 sqlite3_bind_parameter_count(m_stmt), sizeof...(params),
68 m_creationPoint.function_name(),
69 m_creationPoint.file_name(), m_creationPoint.line()));
72 // (gcc16 will warn that param_idx is unused if params is empty.)
73 [[maybe_unused]] int param_idx = 1;
75 // Using post-increment, which returns the current value of param_idx
76 // then increments before the next call to bind
77 (bind(param_idx++, params), ...);
78 } catch (const std::tuple<int, int>& e) {
79 std::string err_msg = std::format(
80 "Error while binding parameter {} of {}: {} ({})"
81 "Statement defined at {} [{}:{}]",
82 std::get<1>(e), sizeof...(params), sqlite3_errstr(std::get<0>(e)),
83 sqlite3_errmsg(m_db), m_creationPoint.function_name(),
84 m_creationPoint.file_name(), m_creationPoint.line());
85 if (std::get<0>(e) == SQLITE_RANGE || std::get<0>(e) == SQLITE_MISUSE) {
86 throw std::logic_error(err_msg);
88 throw std::runtime_error(err_msg);
90 // ensure number of result arguments is correct
91 if (sqlite3_column_count(m_stmt) != sizeof...(ReturnArgs)) {
92 throw std::logic_error(
93 std::format("Return type count mismatch in SQLite statement execution: "
94 "got {} but need {}\n"
95 "Statement defined at {} [{}:{}]",
96 sizeof...(params), sqlite3_column_count(m_stmt),
97 m_creationPoint.function_name(),
98 m_creationPoint.file_name(), m_creationPoint.line()));
102 if constexpr (sizeof...(ReturnArgs) == 0) {
103 // Loop anyway in case we have a multi-step statement that returns no data
108 ResultType<ReturnArgs...> result{};
110 if (result.empty()) { // i.e. if this is the first row returned
112 std::array<int, sizeof...(ReturnArgs)> sqlColumnTypes{};
113 for (int i = 0; std::size_t(i) < sizeof...(ReturnArgs); ++i) {
114 sqlColumnTypes[i] = sqlite3_column_type(m_stmt, i);
116 std::size_t colIdx = 0;
118 (checkType<ReturnArgs>(sqlColumnTypes[colIdx++]) && ...);
120 throw std::runtime_error(
121 std::format("Return type mismatch (column {} should be {}) in "
122 "SQLite statement execution!\n"
123 "Statement defined at {} [{}:{}]",
124 colIdx - 1, sqlColumnTypes[colIdx - 1],
125 m_creationPoint.function_name(),
126 m_creationPoint.file_name(), m_creationPoint.line()));
129 result.push_back(columns<ReturnArgs...>(
130 std::make_index_sequence<sizeof...(ReturnArgs)>{}));