Belle II Software  release-08-01-10
PostgreSQLInterface.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/psql/PostgreSQLInterface.h"
9 #include "daq/slc/base/ConfigFile.h"
10 
11 #ifndef NOT_USE_PSQL
12 #include <pgsql/libpq-fe.h>
13 #endif
14 
15 #include <daq/slc/base/StringUtil.h>
16 #include <daq/slc/database/DBHandlerException.h>
17 
18 #include <daq/slc/system/LockGuard.h>
19 
20 using namespace Belle2;
21 
22 PostgreSQLInterface::PostgreSQLInterface(const std::string& host,
23  const std::string& database,
24  const std::string& user,
25  const std::string& password,
26  int port)
27 {
28  init(host, database, user, password, port);
29  m_sq_conn = NULL;
30  m_sq_result = NULL;
31 }
32 
33 PostgreSQLInterface::PostgreSQLInterface()
34 {
35  ConfigFile config("slowcontrol");
36  init(config.get("database.host"),
37  config.get("database.dbname"),
38  config.get("database.user"),
39  config.get("database.password"),
40  config.getInt("database.port"));
41  m_sq_conn = NULL;
42  m_sq_result = NULL;
43 }
44 
45 void PostgreSQLInterface::connect()
46 {
47 #ifndef NOT_USE_PSQL
48  if (isConnected()) return;
49 
50  {
51  LockGuard lockGuard(m_mutex);
52  m_sq_conn = PQconnectdb(StringUtil::form("host=%s dbname=%s user=%s password=%s port=%d",
53  m_host.c_str(), m_database.c_str(),
54  m_user.c_str(), m_password.c_str(),
55  m_port).c_str());
56  }
57 
58  if (PQstatus(m_sq_conn) == CONNECTION_BAD) {
59  DBHandlerException exception("Failed to connect to the database : %s",
60  PQerrorMessage(m_sq_conn));
61  close();
62  throw exception;
63  }
64 #else
65  throw (DBHandlerException("PGLIB is not available"));
66 #endif
67 }
68 
69 bool PostgreSQLInterface::isConnected()
70 {
71 #ifndef NOT_USE_PSQL
72  LockGuard lockGuard(m_mutex);
73  bool connected = m_sq_conn != NULL && PQstatus(m_sq_conn) == CONNECTION_OK;
74  return connected;
75 #else
76  return false;
77 #endif
78 }
79 
80 void PostgreSQLInterface::execute_imp(const char* command)
81 {
82  clear();
83 #ifndef NOT_USE_PSQL
84  LockGuard lockGuard(m_mutex);
85  m_sq_result = PQexec(m_sq_conn, command);
86  ExecStatusType status = PQresultStatus(m_sq_result);
87  if (status == PGRES_FATAL_ERROR) {
88  // Need to pre-generate exception to avoid data race
89  // on PQerrorMessage.
90  DBHandlerException exception("Failed to execute command : %s (%s)",
91  command, PQerrorMessage(m_sq_conn));
92  throw (exception);
93  }
94 #else
95  throw (DBHandlerException("libpg is not available"));
96 #endif
97 }
98 
99 DBRecordList PostgreSQLInterface::loadRecords()
100 {
101 #ifndef NOT_USE_PSQL
102  LockGuard lockGuard(m_mutex);
103  if (PQresultStatus(m_sq_result) != PGRES_TUPLES_OK) {
104  throw (DBHandlerException("DB records are not ready for reading"));
105  }
106  const size_t nrecords = PQntuples(m_sq_result);
107  const size_t nfields = PQnfields(m_sq_result);
108  m_record_v = DBRecordList();
109  std::vector<std::string> name_v;
110  for (size_t ifield = 0; ifield < nfields; ifield++) {
111  const char* name = PQfname(m_sq_result, ifield);
112  if (name != NULL) name_v.push_back(name);
113  }
114  for (size_t irecord = 0; irecord < nrecords; irecord++) {
115  DBRecord record;
116  for (size_t ifield = 0; ifield < nfields; ifield++) {
117  if (!PQgetisnull(m_sq_result, irecord, ifield)) {
118  const char* value = PQgetvalue(m_sq_result, irecord, ifield);
119  if (value != NULL) {
120  record.add(name_v[ifield], value);
121  }
122  }
123  }
124  m_record_v.push_back(record);
125  }
126 
127  // Vector copy must be done before mutex release,
128  // otherwise data race is introduced.
129  DBRecordList ret(m_record_v);
130 
131  return ret;
132 #else
133  throw (DBHandlerException("libpg is not available"));
134 #endif
135 }
136 
137 void PostgreSQLInterface::clear()
138 {
139 #ifndef NOT_USE_PSQL
140  LockGuard lockGuard(m_mutex);
141  if (m_sq_result != NULL) {
142  PQclear(m_sq_result);
143  m_sq_result = NULL;
144  }
145 #else
146  throw (DBHandlerException("libpg is not available"));
147 #endif
148 }
149 
150 void PostgreSQLInterface::close()
151 {
152  clear();
153 #ifndef NOT_USE_PSQL
154  LockGuard lockGuard(m_mutex);
155  if (m_sq_conn != NULL) {
156  PQfinish(m_sq_conn);
157  m_sq_conn = NULL;
158  }
159 #else
160  throw (DBHandlerException("libpg is not available"));
161 #endif
162 }
163 
164 bool PostgreSQLInterface::checkTable(const std::string& tablename)
165 {
166 #ifndef NOT_USE_PSQL
167  execute("select relname from pg_stat_user_tables where relname='%s';",
168  tablename.c_str());
169  DBRecordList ret(loadRecords());
170  return ret.size() > 0;
171 #else
172  throw (DBHandlerException("libpg is not available"));
173 #endif
174 }
175 
176 DBFieldTypeList PostgreSQLInterface::getTableContents(const std::string& tablename)
177 {
178 #ifndef NOT_USE_PSQL
179  DBFieldTypeList name_m;
180  execute("select attname, typname from pg_class, pg_attribute, pg_type "
181  "where relkind ='r'and relname = '%s' and attrelid = relfilenode "
182  "and attnum > 0 and pg_type.oid = atttypid;", tablename.c_str());
183  DBRecordList ret(loadRecords());
184  for (size_t i = 0; i < ret.size(); i++) {
185  name_m.insert(DBFieldTypeList::value_type(ret[i].get("attname"),
186  ret[i].get("typname")));
187  }
188  return name_m;
189 #else
190  throw (DBHandlerException("libpg is not available"));
191 #endif
192 }
Lock Guard for a Mutex instance.
Definition: LockGuard.h:47
Abstract base class for different kinds of events.