ATLAS Offline Software
Statement.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 #include <format>
5 
6 
7 namespace SQLite {
8 namespace {
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;
19  } else {
20  return false;
21  }
22 }
23 } // namespace
24 
25 template <typename I>
26  requires std::integral<I>
27 void Statement::bind(int index, I value) {
28  bind(index, std::int64_t(value));
29 }
30 
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));
39  } else {
40  static_assert(false,
41  "Unsupported result type for column. Supported types are "
42  "std::int64_t, double, and std::string");
43  }
44 }
45 
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>()...);
49 }
50 
51 template <ValidColumnType... ReturnArgs, ValidParamType... ParamArgs>
52 ResultTypeWrapper<ReturnArgs...>::type Statement::run(ParamArgs... params) {
53  if (!m_stmt) {
54  throw std::logic_error("Can't run unfilled Statement!");
55  }
56  // Lock mutex for thread safety
57  std::lock_guard lck{m_stmtMutex};
58  // Reset everything
59  reset();
60 
61  // Handle parameters
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()));
70  }
71  // bind parameters
72  int param_idx = 1;
73  try {
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);
86  }
87  throw std::runtime_error(err_msg);
88  }
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()));
98  }
99 
100  // Run statement
101  if constexpr (sizeof...(ReturnArgs) == 0) {
102  // Loop anyway in case we have a multi-step statement that returns no data
103  while (step()) {
104  }
105  return;
106  } else {
107  ResultType<ReturnArgs...> result{};
108  while (step()) {
109  if (result.empty()) { // i.e. if this is the first row returned
110  // Check types match
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);
114  }
115  std::size_t colIdx = 0;
116  const bool typesOK =
117  (checkType<ReturnArgs>(sqlColumnTypes[colIdx++]) && ...);
118  if (!typesOK) {
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()));
126  }
127  }
128  result.push_back(columns<ReturnArgs...>(
129  std::make_index_sequence<sizeof...(ReturnArgs)>{}));
130  }
131  return result;
132  }
133 }
134 } // namespace SQLite