Belle II Software  release-08-01-10
DBObjectLoader.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 #include "daq/slc/database/DBObjectLoader.h"
9 
10 #include <daq/slc/system/LogFile.h>
11 
12 #include <daq/slc/base/StringUtil.h>
13 #include <daq/slc/base/ConfigFile.h>
14 #include <daq/slc/database/DBHandlerException.h>
15 
16 #include <iostream>
17 #include <sstream>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <stdexcept>
21 #include <daq/slc/system/LockGuard.h>
22 
23 using namespace Belle2;
24 
25 Mutex DBObjectLoader::m_mutex;
26 
27 DBObject DBObjectLoader::load(const std::string& filename)
28 {
29  ConfigFile config(filename);
30  return load(config);
31 }
32 
33 DBObject DBObjectLoader::load(ConfigFile& config)
34 {
35  const std::string nodename = config.get("nodename");
36  const std::string configname = config.get("config");
37  DBObject obj;
38  if (nodename.size() > 0)
39  obj.setName(nodename + "@" + configname);
40  else
41  obj.setName(configname);
42  for (StringList::iterator it = config.getLabels().begin();
43  it != config.getLabels().end(); ++it) {
44  const std::string name = *it;
45  StringList str = StringUtil::split(name, '.');
46  if (str[0] == "config") continue;
47  if (str[0] == "nodename") continue;
48  std::string value = config.get(name);
49  std::string::size_type pos = value.find_first_of("(");
50  DBField::Type type = DBField::TEXT;
51  if (pos != std::string::npos) {
52  std::string type_s = value.substr(0, pos);
53  if (type_s == "bool") type = DBField::BOOL;
54  else if (type_s == "int") type = DBField::INT;
55  else if (type_s == "float") type = DBField::FLOAT;
56  else if (type_s == "double") type = DBField::DOUBLE;
57  else if (type_s == "object") type = DBField::OBJECT;
58  if (type != DBField::TEXT) {
59  value = StringUtil::replace(value.substr(pos + 1), ")", "");
60  }
61  } else {
62  float vf;
63  if (StringUtil::tolower(value) == "true") {
64  type = DBField::BOOL;
65  } else if (StringUtil::tolower(value) == "false") {
66  type = DBField::BOOL;
67  } else if ((value.size() >= 2 && value.at(0) == '0' && value.at(1) == 'x') ||
68  StringUtil::isdigit(value)) {
69  type = DBField::INT;
70  } else if (StringUtil::split(value, '.').size() < 3 &&
71  StringUtil::isdigit(StringUtil::replace(value, ".", "")) &&
72  sscanf(value.c_str(), "%f", &vf) == 1) {
73  type = DBField::DOUBLE;
74  } else {
75  type = DBField::TEXT;
76  }
77  }
78  if (!setObject(obj, str, type, value)) {
79  LogFile::error("error : %s : %s", name.c_str(), value.c_str());
80  }
81  }
82  return obj;
83 }
84 
85 DBObject DBObjectLoader::load(DBInterface& db,
86  const std::string& tablename,
87  const std::string& config_in, bool /*isfull*/)
88 {
89  std::string configname = config_in;
90  if (!db.isConnected()) {
91  db.connect();
92  }
93  DBObject obj;
94  StringList list = DBObjectLoader::getDBlist(db, tablename, configname);
95  if (list.size() == 0) return obj;
96  StringList s = StringUtil::split(list[0], ',');
97  DBRecordList record_v;
98  std::stringstream ss;
99  ss << "select * from " << s[1] << " where pid =" << s[2] << " order by id";
100  try {
101  LockGuard lockGuard(m_mutex);
102  db.execute(ss.str());
103  record_v = db.loadRecords();
104  } catch (const DBHandlerException& e) {
105  throw;
106  }
107  int timestamp = 0;
108  if (list.size() > 0) {
109  configname = s[0];
110  timestamp = atoi(s[4].c_str());
111  }
112  ss.str("");
113  ss << " config : " << configname << std::endl;
114  for (size_t i = 0; i < record_v.size(); i++) {
115  DBRecord& record(record_v[i]);
116  if (record.hasField("value_b")) {
117  ss << record.get("name") << " : " <<
118  (record.getBool("value_b") ? "true" : "false") << std::endl;
119  } else if (record.hasField("value_i")) {
120  ss << record.get("name") << " : int(" << record.get("value_i") << ")" << std::endl;
121  } else if (record.hasField("value_f")) {
122  ss << record.get("name") << " : double(" << record.get("value_f") << ")" << std::endl;
123  } else if (record.hasField("value_t")) {
124  ss << record.get("name") << " : \"" << record.get("value_t") << "\"" << std::endl;
125  }
126  }
127  ConfigFile conf(ss);
128  obj = DBObjectLoader::load(conf);
129  obj.setDate(timestamp);
130  return obj;
131 }
132 
133 bool DBObjectLoader::add(DBObject& obj, StringList& str,
134  const std::string& name_in, const DBObject& cobj)
135 {
136  std::string name = str[0];
137  int index = 0;
138  StringList sstr = StringUtil::split(str[0], '[');
139  if (sstr.size() > 1) {
140  index = atoi(sstr[1].c_str());
141  name = sstr[0];
142  } else {
143  name = str[0];
144  index = 0;
145  }
146  if (str.size() > 1) {
147  str.erase(str.begin());
148  if (obj.hasObject(name)) {
149  return add(obj.getObject(name, index), str, name_in, cobj);
150  }
151  throw (std::out_of_range(StringUtil::form("%s:%d %s", __FILE__, __LINE__,
152  name.c_str())));
153  } else {
154  if (obj.hasObject(name) && obj.getNObjects(name) > index) {
155  obj.getObject(name, index).addObject(name_in, cobj);
156  return true;
157  }
158  obj.addObject(name, cobj);
159  return true;
160  }
161  throw (std::out_of_range(StringUtil::form("%s:%d %s", __FILE__, __LINE__,
162  StringUtil::join(str, ".").c_str())));
163 }
164 
165 bool DBObjectLoader::setObject(DBObject& obj, StringList& str,
166  DBField::Type type, const std::string& value,
167  const std::string& table_in, const std::string& config_in,
168  DBInterface* db)
169 {
170  std::string name;
171  int index = 0;
172  if (str.size() > 0) {
173  StringList sstr = StringUtil::split(str[0], '[');
174  if (sstr.size() > 1) {
175  index = atoi(sstr[1].c_str());
176  name = sstr[0];
177  } else {
178  name = str[0];
179  index = 0;
180  }
181  } else {
182  return false;
183  }
184  if (str.size() > 1) {
185  str.erase(str.begin());
186  bool found = obj.hasObject(name);
187  if (found) {
188  DBObjectList& objs(obj.getObjects(name));
189  for (DBObjectList::iterator it = objs.begin();
190  it != objs.end(); ++it) {
191  if (it->getIndex() == index) {
192  found = false;
193  break;
194  }
195  }
196  found = !found;
197  }
198  if (!found) {
199  DBObject cobj;
200  cobj.setName(name);
201  cobj.setIndex(index);
202  obj.addObject(name, cobj);
203  }
204  DBObjectList& objs(obj.getObjects(name));
205  for (DBObjectList::iterator it = objs.begin();
206  it != objs.end(); ++it) {
207  DBObject& cobj(*it);
208  if (cobj.getIndex() == index) {
209  return setObject(cobj, str, type, value, table_in, config_in, db);
210  }
211  }
212  } else {
213  switch (type) {
214  case DBField::BOOL: obj.addBool(name, false); break;
215  case DBField::INT: obj.addInt(name, 0); break;
216  case DBField::FLOAT: obj.addFloat(name, 0); break;
217  case DBField::DOUBLE: obj.addDouble(name, 0); break;
218  case DBField::TEXT: obj.addText(name, value); break;
219  case DBField::OBJECT: {
220  DBObject cobj(value);
221  std::string config_out = config_in;
222  //LogFile::notice(config_out);
223  if (db) {
224  StringList list = DBObjectLoader::getDBlist(*db, table_in, config_out);
225  if (list.size() > 0) {
226  //LogFile::notice("%s:%d", __FILE__, __LINE__);
227  bool found = false;
228  for (size_t i = 0; i < list.size(); i++) {
229  if (config_out == list[0]) {
230  found = true;
231  break;
232  }
233  }
234  if (found) {
235  cobj = DBObjectLoader::load(*db, table_in, config_out, true);
236  } else {
237  if (list.size() > 0) {
238  config_out = list[0];
239  cobj = DBObjectLoader::load(*db, table_in, config_out, true);
240  }
241  }
242  }
243  }
244  if (cobj.hasObject(name)) {
245  cobj.getObject(name).setPath(table_in + "/" + config_out);
246  obj.addObjects(name, cobj.getObjects(name));
247  } else {
248  cobj.setName(name);
249  obj.addObject(name, cobj);
250  }
251  } break;
252  default : return false;
253  }
254  obj.setValueText(name, value);
255  return true;
256  }
257  return false;
258 }
259 
260 bool DBObjectLoader::createDB(DBInterface& db,
261  const std::string& tablename,
262  const DBObject& obj)
263 {
264  std::string tablename_date = tablename + "_" + Date().toString("%Y");
265  std::string tablename_id = tablename + "_id";
266  try {
267  if (!db.isConnected()) db.connect();
268  } catch (const DBHandlerException& e) {
269  LogFile::error(e.what());
270  return false;
271  }
272  try {
273  if (obj.getName().size() == 0) {
274  LogFile::error("Configname is null. createDB canceled.");
275  return false;
276  }
277  if (!db.checkTable("daqconfig")) {
278  db.execute("create table daqconfig \n"
279  "(name text not null, \n"
280  "id bigserial, lastupdate timestamp, \n"
281  "UNIQUE(name));");
282  db.execute("create index daqconfig_id_index on daqconfig(id);",
283  tablename.c_str(), tablename.c_str());
284  }
285  if (!db.checkTable(tablename_id)) {
286  try {
287  db.execute("insert into daqconfig (name, lastupdate) values "
288  "('%s', current_timestamp);", tablename_id.c_str());
289  } catch (const std::exception& e) {
290  db.execute("update daqconfig set lastupdate = current_timestamp where name = '%s';",
291  tablename_id.c_str());
292  }
293  db.execute("create table %s \n"
294  "(name text not null \n"
295  "check (replace(name, '.', '') = name) not null, \n"
296  "id bigserial, \n"
297  "content text, \n"
298  "record_time timestamp with time zone default current_timestamp, \n"
299  "UNIQUE (name)); ", tablename_id.c_str());
300  }
301  if (!db.checkTable(tablename_date)) {
302  try {
303  db.execute("insert into daqconfig (name, lastupdate) values "
304  "('%s', current_timestamp);", tablename_date.c_str());
305  } catch (const std::exception& e) {
306  db.execute("update daqconfig set lastupdate = current_timestamp where name = '%s';",
307  tablename_date.c_str());
308  }
309  db.execute("create table %s \n"
310  "(name text not null,\n"
311  "id bigserial, \n"
312  "pid bigint, \n"
313  "value_b boolean default NULL, \n"
314  "value_i int default NULL, \n"
315  "value_f float default NULL, \n"
316  "value_t text default NULL \n"
317  "); ", tablename_date.c_str());
318  db.execute("create index %s_index on %s(id);",
319  tablename_date.c_str(), tablename_date.c_str());
320  }
321  } catch (const DBHandlerException& e) {
322  LogFile::warning(e.what());
323  }
324  try {
325  db.execute("begin;");
326  bool failed = true;
327  int id = 1;
328  try {
329  db.execute("insert into %s (name, content) values ('%s','%s') returning id;",
330  tablename_id.c_str(), obj.getName().c_str(), tablename_date.c_str());
331  failed = false;
332  DBRecordList record(db.loadRecords());
333  if (record.size() > 0) {
334  id = record[0].getInt("id");
335  }
336  } catch (const DBHandlerException& e) {
337  LogFile::error(e.what());
338  }
339  db.execute("commit;");
340  if (failed) return false;
341  std::string s = obj.printSQL(tablename_date, id);
342  db.execute(s);
343  } catch (const DBHandlerException& e) {
344  LogFile::error(e.what());
345  return false;
346  }
347  return true;
348 }
349 
350 StringList DBObjectLoader::getDBlist(DBInterface& db,
351  const std::string& tablename,
352  const std::string& grep, int max)
353 {
354  StringList str;
355  try {
356  if (!db.isConnected()) db.connect();
357  std::stringstream ss;
358  if (grep.size() > 0) {
359  const char* prefix = grep.c_str();
360  ss << "select id,name,content,to_char(record_time,'DD/MM HH24:MI:SS') as tdate, extract(epoch from record_time) as utime from " <<
361  tablename << "_id where "
362  << "(name like '" << prefix << "_%' or "
363  << "name like '" << prefix << "') order by id desc";
364  if (max > 0) ss << " limit " << max;
365  ss << ";";
366  } else {
367  ss << "select id,name,content,to_char(record_time,'DD/MM HH24:MI:SS') as tdate, extract(epoch from record_time) as utime from " <<
368  tablename << "_id order by id desc";
369  if (max > 0) ss << " limit " << max;
370  ss << ";";
371  }
372  LockGuard lockGuard(m_mutex);
373  db.execute(ss.str());
374  DBRecordList record_v(db.loadRecords());
375  for (size_t i = 0; i < record_v.size(); i++) {
376  DBRecord& record(record_v[i]);
377  str.push_back(record.get("name") + "," + record.get("content") + "," +
378  record.get("id") + "," + record.get("tdate") + "," + record.get("utime"));
379  }
380  } catch (const DBHandlerException& e) {
381  LogFile::error(e.what());
382  }
383  return str;
384 }
Lock Guard for a Mutex instance.
Definition: LockGuard.h:47
Abstract base class for different kinds of events.