2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
9 // Utility function to compare SQLite type to C++ type
10 template <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>
27 void Statement::bind(int index, I value) {
28 bind(index, std::int64_t(value));
31 template <ValidColumnType T, std::size_t I>
32 T 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");
46 template <ValidColumnType... Ts, std::size_t... Is>
47 std::tuple<Ts...> Statement::columns(std::index_sequence<Is...>) {
48 return std::make_tuple<Ts...>(column<Ts, Is>()...);
51 template <ValidColumnType... ReturnArgs, ValidParamType... ParamArgs>
52 ResultTypeWrapper<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()));
74 // Using post-increment, which returns the current value of param_idx
75 // then increments before the next call to bind
76 (bind(param_idx++, params), ...);
77 } catch (const std::tuple<int, int>& e) {
78 std::string err_msg = std::format(
79 "Error while binding parameter {} of {}: {} ({})"
80 "Statement defined at {} [{}:{}]",
81 std::get<1>(e), sizeof...(params), sqlite3_errstr(std::get<0>(e)),
82 sqlite3_errmsg(m_db), m_creationPoint.function_name(),
83 m_creationPoint.file_name(), m_creationPoint.line());
84 if (std::get<0>(e) == SQLITE_RANGE || std::get<0>(e) == SQLITE_MISUSE) {
85 throw std::logic_error(err_msg);
87 throw std::runtime_error(err_msg);
89 // ensure number of result arguments is correct
90 if (sqlite3_column_count(m_stmt) != sizeof...(ReturnArgs)) {
91 throw std::logic_error(
92 std::format("Return type count mismatch in SQLite statement execution: "
93 "got {} but need {}\n"
94 "Statement defined at {} [{}:{}]",
95 sizeof...(params), sqlite3_column_count(m_stmt),
96 m_creationPoint.function_name(),
97 m_creationPoint.file_name(), m_creationPoint.line()));
101 if constexpr (sizeof...(ReturnArgs) == 0) {
102 // Loop anyway in case we have a multi-step statement that returns no data
107 ResultType<ReturnArgs...> result{};
109 if (result.empty()) { // i.e. if this is the first row returned
111 std::array<int, sizeof...(ReturnArgs)> sqlColumnTypes{};
112 for (int i = 0; std::size_t(i) < sizeof...(ReturnArgs); ++i) {
113 sqlColumnTypes[i] = sqlite3_column_type(m_stmt, i);
115 std::size_t colIdx = 0;
117 (checkType<ReturnArgs>(sqlColumnTypes[colIdx++]) && ...);
119 throw std::runtime_error(
120 std::format("Return type mismatch (column {} should be {}) in "
121 "SQLite statement execution!\n"
122 "Statement defined at {} [{}:{}]",
123 colIdx - 1, sqlColumnTypes[colIdx - 1],
124 m_creationPoint.function_name(),
125 m_creationPoint.file_name(), m_creationPoint.line()));
128 result.push_back(columns<ReturnArgs...>(
129 std::make_index_sequence<sizeof...(ReturnArgs)>{}));
134 } // namespace SQLite