LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
ParameterSetRegistry.cc
Go to the documentation of this file.
2 
3 #include "cetlib/container_algorithms.h"
4 #include "cetlib/sqlite/Transaction.h"
5 #include "cetlib/sqlite/column.h"
6 #include "cetlib/sqlite/create_table.h"
7 #include "cetlib/sqlite/exec.h"
8 #include "cetlib/sqlite/query_result.h"
9 #include "cetlib/sqlite/select.h"
11 #include "fhiclcpp/exception.h"
12 
13 #include "sqlite3.h"
14 
15 #include <cassert>
16 #include <iostream>
17 
19 
20 std::recursive_mutex fhicl::ParameterSetRegistry::mutex_{};
21 
22 namespace {
23  sqlite3*
24  openPrimaryDB()
25  {
26  sqlite3* result = nullptr;
27  sqlite3_open(":memory:", &result);
28  throwOnSQLiteFailure(result);
29  using namespace cet::sqlite;
30  Transaction txn{result};
31  create_table(result,
32  "ParameterSets",
33  column<std::string, primary_key>{"ID"},
34  column<std::string>{"PSetBlob"});
35  txn.commit();
36  return result;
37  }
38 }
39 
40 void
41 fhicl::detail::throwOnSQLiteFailure(int const errcode, char* msg)
42 {
43  std::string const msgString{msg ? msg : ""};
44  sqlite3_free(msg);
45  if (errcode != SQLITE_OK) {
46  // Caller's responsibility to make sure this really is an error
47  // and not (say) SQLITE_ROW or SQLITE_DONE,
48  throw exception(error::sql_error, "SQLite error:")
49  << sqlite3_errstr(errcode) << " (" << errcode << ')'
50  << (msgString.empty() ? "" : (std::string(": ") + msgString));
51  }
52 }
53 
54 void
55 fhicl::detail::throwOnSQLiteFailure(sqlite3* db, char* msg)
56 {
57  if (db == nullptr) {
58  sqlite3_free(msg);
59  throw fhicl::exception(fhicl::error::cant_open_db) << "Can't open DB.";
60  }
61  auto const errcode = sqlite3_errcode(db);
62  throwOnSQLiteFailure(errcode, msg);
63 }
64 
66 {
67  sqlite3_finalize(stmt_);
68  try {
69  throwOnSQLiteFailure(primaryDB_);
70  }
71  catch (fhicl::exception const& e) {
72  std::cerr << e.what() << '\n';
73  }
74  catch (...) {
75  }
76  int retcode;
77  do {
78  retcode = sqlite3_close(primaryDB_);
79  } while (retcode == SQLITE_BUSY);
80 }
81 
82 void
84 {
85  assert(db);
86  std::lock_guard sentry{mutex_};
87 
88  // This does *not* cause anything new to be imported into the
89  // registry itself, just its backing DB.
90  sqlite3_stmt* oStmt = nullptr;
91  sqlite3* primaryDB = instance_().primaryDB_;
92 
93  // Index constraint on ID will prevent duplicates via INSERT OR IGNORE.
94  sqlite3_prepare_v2(
95  primaryDB,
96  "INSERT OR IGNORE INTO ParameterSets(ID, PSetBlob) VALUES(?, ?);",
97  -1,
98  &oStmt,
99  nullptr);
100  throwOnSQLiteFailure(primaryDB);
101 
102  using namespace cet::sqlite;
103  query_result<std::string, std::string> inputPSes;
104  inputPSes << select("*").from(db, "ParameterSets");
105 
106  for (auto const& [idString, psBlob] : inputPSes) {
107  sqlite3_bind_text(
108  oStmt, 1, idString.c_str(), idString.size() + 1, SQLITE_STATIC);
109  throwOnSQLiteFailure(primaryDB);
110  sqlite3_bind_text(
111  oStmt, 2, psBlob.c_str(), psBlob.size() + 1, SQLITE_STATIC);
112  throwOnSQLiteFailure(primaryDB);
113  switch (sqlite3_step(oStmt)) {
114  case SQLITE_DONE:
115  break; // OK
116  default:
117  throwOnSQLiteFailure(primaryDB);
118  }
119  sqlite3_reset(oStmt);
120  throwOnSQLiteFailure(primaryDB);
121  }
122  sqlite3_finalize(oStmt);
123  throwOnSQLiteFailure(primaryDB);
124 }
125 
126 void
128 {
129  assert(db);
130  std::lock_guard sentry{mutex_};
131 
132  cet::sqlite::Transaction txn{db};
133  cet::sqlite::exec(db,
134  "DROP TABLE IF EXISTS ParameterSets;"
135  "CREATE TABLE ParameterSets(ID PRIMARY KEY, PSetBlob);");
136  txn.commit();
137 
138  sqlite3_stmt* oStmt = nullptr;
139  sqlite3_prepare_v2(
140  db,
141  "INSERT OR IGNORE INTO ParameterSets(ID, PSetBlob) VALUES(?, ?);",
142  -1,
143  &oStmt,
144  nullptr);
146 
147  // Calling to_compact_string() can insert entries into the registry
148  // container. If that insertion causes a rehash, the iterators are
149  // invalidated. We therefore need to start over in that case and
150  // make sure we can get through the entire list without causing a
151  // rehash.
152  while (std::any_of(get().begin(), get().end(), [oStmt, db](auto const& p) {
153  // The rehash threshold is defined by the STL
154  // [container.requirements.unord.req.general]
155  auto const current_rehash_threshold =
156  get().max_load_factor() * get().bucket_count();
157  std::string id(p.first.to_string());
158  std::string psBlob(p.second.to_compact_string());
159  if (get().size() > current_rehash_threshold) {
160  return true;
161  }
162  sqlite3_bind_text(oStmt, 1, id.c_str(), id.size() + 1, SQLITE_STATIC);
164  sqlite3_bind_text(
165  oStmt, 2, psBlob.c_str(), psBlob.size() + 1, SQLITE_STATIC);
167  switch (sqlite3_step(oStmt)) {
168  case SQLITE_DONE:
169  sqlite3_reset(oStmt);
171  break; // OK
172  default:
174  }
175  return false;
176  })) {
177  }
178 
179  sqlite3* const primaryDB{instance_().primaryDB_};
180  using namespace cet::sqlite;
181  query_result<std::string, std::string> regPSes;
182  regPSes << select("*").from(primaryDB, "ParameterSets");
183 
184  for (auto const& [idString, psBlob] : regPSes) {
185  sqlite3_bind_text(
186  oStmt, 1, idString.c_str(), idString.size() + 1, SQLITE_STATIC);
188  sqlite3_bind_text(
189  oStmt, 2, psBlob.c_str(), psBlob.size() + 1, SQLITE_STATIC);
191  switch (sqlite3_step(oStmt)) {
192  case SQLITE_DONE:
193  sqlite3_reset(oStmt);
195  break; // OK
196  default:
198  }
199  }
200  sqlite3_finalize(oStmt);
202 }
203 
204 void
206 {
207  std::lock_guard sentry{mutex_};
208 
209  sqlite3* primaryDB = instance_().primaryDB_;
210  auto& registry = instance_().registry_;
211  using namespace cet::sqlite;
212  query_result<std::string, std::string> entriesToStageIn;
213  entriesToStageIn << select("*").from(primaryDB, "ParameterSets");
214 
215  cet::transform_all(entriesToStageIn,
216  std::inserter(registry, std::begin(registry)),
217  [](auto const& row) {
218  auto const& [idString, psBlob] = row;
219  auto const pset = ParameterSet::make(psBlob);
220  return std::make_pair(ParameterSetID{idString}, pset);
221  });
222 }
223 
225  : primaryDB_{openPrimaryDB()}
226 {}
227 
228 auto
230 {
231  // No lock here -- it was already acquired by get(...).
232  auto it = registry_.find(id);
233  if (it == registry_.cend()) {
234  // Look in primary DB for this ID and its contained IDs.
235  if (stmt_ == nullptr) {
236  sqlite3_prepare_v2(primaryDB_,
237  "SELECT PSetBlob FROM ParameterSets WHERE ID = ?;",
238  -1,
239  &stmt_,
240  nullptr);
242  }
243  auto idString = id.to_string();
244  auto result = sqlite3_bind_text(
245  stmt_, 1, idString.c_str(), idString.size() + 1, SQLITE_STATIC);
247  result = sqlite3_step(stmt_);
248  switch (result) {
249  case SQLITE_ROW: // Found the ID in the DB.
250  {
251  auto const pset = ParameterSet::make(
252  reinterpret_cast<char const*>(sqlite3_column_text(stmt_, 0)));
253  // Put into the registry without triggering ParameterSet::id().
254  it = registry_.emplace(id, pset).first;
255  } break;
256  case SQLITE_DONE:
257  break; // Not here.
258  default:
260  }
261  sqlite3_reset(stmt_);
262  }
263  return it;
264 }
std::unique_ptr< InputSource > make(fhicl::ParameterSet const &conf, InputSourceDescription &desc)
static ParameterSet make(intermediate_table const &tbl)
Definition: ParameterSet.cc:68
collection_type::const_iterator const_iterator
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:101
static std::recursive_mutex mutex_
void throwOnSQLiteFailure(int rc, char *msg=nullptr)
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:69
const_iterator find_(ParameterSetID const &id)
Float_t e
Definition: plot.C:35
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
static void exportTo(sqlite3 *db)
static void importFrom(sqlite3 *db)