Belle II Software development
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
23using namespace Belle2;
24
25Mutex DBObjectLoader::m_mutex;
26
27DBObject DBObjectLoader::load(const std::string& filename)
28{
29 ConfigFile config(filename);
30 return load(config);
31}
32
33DBObject 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
85DBObject 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
133bool DBObjectLoader::add(DBObject& obj, StringList& str,
134 const std::string& name_in, const DBObject& cobj)
135{
136 std::string name;
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 index = 0;
144 name = str[0];
145 }
146
147 if (str.size() > 1) {
148 str.erase(str.begin());
149 if (obj.hasObject(name)) {
150 return add(obj.getObject(name, index), str, name_in, cobj);
151 }
152 throw (std::out_of_range(StringUtil::form("%s:%d %s", __FILE__, __LINE__,
153 name.c_str())));
154 } else {
155 if (obj.hasObject(name) && obj.getNObjects(name) > index) {
156 obj.getObject(name, index).addObject(name_in, cobj);
157 return true;
158 }
159 obj.addObject(name, cobj);
160 return true;
161 }
162 throw (std::out_of_range(StringUtil::form("%s:%d %s", __FILE__, __LINE__,
163 StringUtil::join(str, ".").c_str())));
164}
165
166bool DBObjectLoader::setObject(DBObject& obj, StringList& str,
167 DBField::Type type, const std::string& value,
168 const std::string& table_in, const std::string& config_in,
169 DBInterface* db)
170{
171 std::string name;
172 int index = 0;
173 if (str.size() > 0) {
174 StringList sstr = StringUtil::split(str[0], '[');
175 if (sstr.size() > 1) {
176 index = atoi(sstr[1].c_str());
177 name = sstr[0];
178 } else {
179 name = str[0];
180 index = 0;
181 }
182 } else {
183 return false;
184 }
185 if (str.size() > 1) {
186 str.erase(str.begin());
187 bool found = obj.hasObject(name);
188 if (found) {
189 DBObjectList& objs(obj.getObjects(name));
190 for (DBObjectList::iterator it = objs.begin();
191 it != objs.end(); ++it) {
192 if (it->getIndex() == index) {
193 found = false;
194 break;
195 }
196 }
197 found = !found;
198 }
199 if (!found) {
200 DBObject cobj;
201 cobj.setName(name);
202 cobj.setIndex(index);
203 obj.addObject(name, cobj);
204 }
205 DBObjectList& objs(obj.getObjects(name));
206 for (DBObjectList::iterator it = objs.begin();
207 it != objs.end(); ++it) {
208 DBObject& cobj(*it);
209 if (cobj.getIndex() == index) {
210 return setObject(cobj, str, type, value, table_in, config_in, db);
211 }
212 }
213 } else {
214 switch (type) {
215 case DBField::BOOL: obj.addBool(name, false); break;
216 case DBField::INT: obj.addInt(name, 0); break;
217 case DBField::FLOAT: obj.addFloat(name, 0); break;
218 case DBField::DOUBLE: obj.addDouble(name, 0); break;
219 case DBField::TEXT: obj.addText(name, value); break;
220 case DBField::OBJECT: {
221 DBObject cobj(value);
222 std::string config_out = config_in;
223 //LogFile::notice(config_out);
224 if (db) {
225 StringList list = DBObjectLoader::getDBlist(*db, table_in, config_out);
226 if (list.size() > 0) {
227 //LogFile::notice("%s:%d", __FILE__, __LINE__);
228 bool found = false;
229 for (size_t i = 0; i < list.size(); i++) {
230 if (config_out == list[0]) {
231 found = true;
232 break;
233 }
234 }
235 if (found) {
236 cobj = DBObjectLoader::load(*db, table_in, config_out, true);
237 } else {
238 if (list.size() > 0) {
239 config_out = list[0];
240 cobj = DBObjectLoader::load(*db, table_in, config_out, true);
241 }
242 }
243 }
244 }
245 if (cobj.hasObject(name)) {
246 cobj.getObject(name).setPath(table_in + "/" + config_out);
247 obj.addObjects(name, cobj.getObjects(name));
248 } else {
249 cobj.setName(name);
250 obj.addObject(name, cobj);
251 }
252 } break;
253 default : return false;
254 }
255 obj.setValueText(name, value);
256 return true;
257 }
258 return false;
259}
260
261bool DBObjectLoader::createDB(DBInterface& db,
262 const std::string& tablename,
263 const DBObject& obj)
264{
265 std::string tablename_date = tablename + "_" + Date().toString("%Y");
266 std::string tablename_id = tablename + "_id";
267 try {
268 if (!db.isConnected()) db.connect();
269 } catch (const DBHandlerException& e) {
270 LogFile::error(e.what());
271 return false;
272 }
273 try {
274 if (obj.getName().size() == 0) {
275 LogFile::error("Configname is null. createDB canceled.");
276 return false;
277 }
278 if (!db.checkTable("daqconfig")) {
279 db.execute("create table daqconfig \n"
280 "(name text not null, \n"
281 "id bigserial, lastupdate timestamp, \n"
282 "UNIQUE(name));");
283 db.execute("create index daqconfig_id_index on daqconfig(id);",
284 tablename.c_str(), tablename.c_str());
285 }
286 if (!db.checkTable(tablename_id)) {
287 try {
288 db.execute("insert into daqconfig (name, lastupdate) values "
289 "('%s', current_timestamp);", tablename_id.c_str());
290 } catch (const std::exception& e) {
291 db.execute("update daqconfig set lastupdate = current_timestamp where name = '%s';",
292 tablename_id.c_str());
293 }
294 db.execute("create table %s \n"
295 "(name text not null \n"
296 "check (replace(name, '.', '') = name) not null, \n"
297 "id bigserial, \n"
298 "content text, \n"
299 "record_time timestamp with time zone default current_timestamp, \n"
300 "UNIQUE (name)); ", tablename_id.c_str());
301 }
302 if (!db.checkTable(tablename_date)) {
303 try {
304 db.execute("insert into daqconfig (name, lastupdate) values "
305 "('%s', current_timestamp);", tablename_date.c_str());
306 } catch (const std::exception& e) {
307 db.execute("update daqconfig set lastupdate = current_timestamp where name = '%s';",
308 tablename_date.c_str());
309 }
310 db.execute("create table %s \n"
311 "(name text not null,\n"
312 "id bigserial, \n"
313 "pid bigint, \n"
314 "value_b boolean default NULL, \n"
315 "value_i int default NULL, \n"
316 "value_f float default NULL, \n"
317 "value_t text default NULL \n"
318 "); ", tablename_date.c_str());
319 db.execute("create index %s_index on %s(id);",
320 tablename_date.c_str(), tablename_date.c_str());
321 }
322 } catch (const DBHandlerException& e) {
323 LogFile::warning(e.what());
324 }
325 try {
326 db.execute("begin;");
327 bool failed = true;
328 int id = 1;
329 try {
330 db.execute("insert into %s (name, content) values ('%s','%s') returning id;",
331 tablename_id.c_str(), obj.getName().c_str(), tablename_date.c_str());
332 failed = false;
333 DBRecordList record(db.loadRecords());
334 if (record.size() > 0) {
335 id = record[0].getInt("id");
336 }
337 } catch (const DBHandlerException& e) {
338 LogFile::error(e.what());
339 }
340 db.execute("commit;");
341 if (failed) return false;
342 std::string s = obj.printSQL(tablename_date, id);
343 db.execute(s);
344 } catch (const DBHandlerException& e) {
345 LogFile::error(e.what());
346 return false;
347 }
348 return true;
349}
350
351StringList DBObjectLoader::getDBlist(DBInterface& db,
352 const std::string& tablename,
353 const std::string& grep, int max)
354{
355 StringList str;
356 try {
357 if (!db.isConnected()) db.connect();
358 std::stringstream ss;
359 if (grep.size() > 0) {
360 const char* prefix = grep.c_str();
361 ss << "select id,name,content,to_char(record_time,'DD/MM HH24:MI:SS') as tdate, extract(epoch from record_time) as utime from " <<
362 tablename << "_id where "
363 << "(name like '" << prefix << "_%' or "
364 << "name like '" << prefix << "') order by id desc";
365 if (max > 0) ss << " limit " << max;
366 ss << ";";
367 } else {
368 ss << "select id,name,content,to_char(record_time,'DD/MM HH24:MI:SS') as tdate, extract(epoch from record_time) as utime from " <<
369 tablename << "_id order by id desc";
370 if (max > 0) ss << " limit " << max;
371 ss << ";";
372 }
373 LockGuard lockGuard(m_mutex);
374 db.execute(ss.str());
375 DBRecordList record_v(db.loadRecords());
376 for (size_t i = 0; i < record_v.size(); i++) {
377 DBRecord& record(record_v[i]);
378 str.push_back(record.get("name") + "," + record.get("content") + "," +
379 record.get("id") + "," + record.get("tdate") + "," + record.get("utime"));
380 }
381 } catch (const DBHandlerException& e) {
382 LogFile::error(e.what());
383 }
384 return str;
385}
Lock Guard for a Mutex instance.
Definition: LockGuard.h:47
Abstract base class for different kinds of events.