ICP  1
ISISDB.cpp
Go to the documentation of this file.
1 // selogcger.cpp : Defines the entry point for the DLL application.
2 //
3 
4 #include "stdafx.h"
5 #include "isisdb.h"
6 
8 static const int db_schema_version = 43;
9 
10 #ifdef _MANAGED
11 #pragma managed(push, off)
12 #endif
13 
14 class DBLock
15 {
16  private:
17  static volatile HANDLE db_mutex;
18 
19  public:
20  DBLock()
21  {
22  static SECURITY_ATTRIBUTES sec_attr = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
23  if (db_mutex == NULL)
24  {
25  db_mutex = CreateMutex(&sec_attr, FALSE, "icpsqlitemutex");
26  }
27  if (db_mutex == NULL)
28  {
29  throw(std::exception("error creating mutex"));
30  }
31  if (WaitForSingleObject(db_mutex, INFINITE) != WAIT_OBJECT_0)
32  {
33  throw(std::exception("error waiting on mutex"));
34  }
35  }
37  {
38  if (ReleaseMutex(db_mutex) == 0)
39  {
40  throw(std::exception("error releasing mutex"));
41  }
42  }
43 };
44 
45 volatile HANDLE DBLock::db_mutex = NULL;
46 
47 const char* ISISDB::version()
48 {
49  static char buffer[256];
50  sprintf(buffer, "ISISDB schema rev %d using SQLITE %s", db_schema_version, SQLITE_VERSION);
51  return buffer;
52 }
53 
55 {
56  DBLock mylock;
57  if (m_db != NULL)
58  {
59  db_list.push_back(m_db);
60  m_db = NULL;
61  }
62 }
63 
65 {
66  char last_error[512];
67  DBLock mylock;
68  if (m_db != NULL)
69  {
70  int rc = sqlite3_close(m_db);
71  m_db = NULL;
72  if (rc != SQLITE_OK)
73  {
74  _snprintf_s(last_error, sizeof(last_error), _TRUNCATE, "SELOGGER: SQLITE error code %d from close", rc);
75  throw(std::exception(last_error));
76  }
77  }
78  return 0;
79 }
80 
81 const char* ISISDB::db_file = "c:\\data\\selog.sq3";
82 
83 std::list<sqlite3*> ISISDB::db_list;
84 
85 #define SQLITE_CHECK_RC(__rc) \
86  if (__rc != SQLITE_OK) \
87  { \
88  _snprintf_s(last_error, sizeof(last_error), _TRUNCATE, "SELOGGER: SQLITE error %s at %s:%d", errmsg, __FILE__, __LINE__); \
89  sqlite3_free(errmsg); \
90  closeDatabase(); \
91  throw std::exception(last_error); \
92  }
93 
94 ISISDB::ISISDB(bool integrity_check) : m_db(NULL)
95 {
96  DBLock mylock;
97  if (db_list.size() > 0)
98  {
99  m_db = db_list.front();
100  db_list.pop_front();
101  return;
102  }
103  char last_error[512];
104  int rc;
105  struct stat stat_struct;
106  char* errmsg = NULL;
107  bool new_file = false;
108  if (stat(db_file, &stat_struct) == -1)
109  {
110  new_file = true;
111  }
112  m_db = NULL;
113  if (sqlite3_threadsafe() == 0)
114  {
115  throw std::exception("sqlite3 not threadsafe");
116  }
117  // use sqlite3 multi-thread mode as we never have the same handle used by two threads at the same time
118  rc = sqlite3_open_v2(db_file, &m_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, NULL);
119  if (rc != SQLITE_OK)
120  {
121  _snprintf_s(last_error, sizeof(last_error), _TRUNCATE, "SELOGGER: Error opening database %s: %s", db_file, sqlite3_errmsg(m_db));
122  m_db = NULL;
123  throw std::exception(last_error);
124  }
125  rc = sqlite3_busy_timeout(m_db, 7 * 24 * 60 * 60 * 1000); // wait up to 1 week for database lock...
126  if (rc != SQLITE_OK)
127  {
128  _snprintf_s(last_error, sizeof(last_error), _TRUNCATE, "SELOGGER: Error setting timeout");
129  closeDatabase();
130  throw std::exception(last_error);
131  }
132  int pnRow, pnColumn;
133  char** pazResult = NULL;
134  if (integrity_check)
135  {
136  rc = sqlite3_get_table(m_db,
137 // "PRAGMA integrity_check;",
138  "PRAGMA quick_check;",
139  &pazResult, &pnRow, &pnColumn, &errmsg);
140  SQLITE_CHECK_RC(rc);
141  if (pnRow == 1 && !strcmp(pazResult[0], "integrity_check"))
142  {
143  sqlite3_free_table(pazResult);
144  }
145  else
146  {
147  std::string s;
148  for(int i=0; i<pnRow; i++)
149  {
150  s.append(pazResult[i]);
151  }
152  _snprintf_s(last_error, sizeof(last_error), _TRUNCATE, "SELOGGER: integrity check failed %s", s.c_str());
153  sqlite3_free_table(pazResult);
154  closeDatabase();
155  throw std::exception(last_error);
156  }
157  }
158  rc = sqlite3_exec(m_db, "PRAGMA cache_size = 40000; "
159  // "PRAGMA synchronous = NORMAL; "
160  "PRAGMA auto_vacuum = FULL; "
161  "PRAGMA journal_mode = WAL; "
162  // "PRAGMA journal_mode = PERSIST; "
163  "PRAGMA temp_store = MEMORY; ", NULL, NULL, &errmsg);
164  SQLITE_CHECK_RC(rc);
165  if (!new_file)
166  {
167  int version = -1;
168  rc = sqlite3_get_table(m_db,
169  "SELECT * FROM config WHERE name = 'version';",
170  &pazResult, &pnRow, &pnColumn, &errmsg);
171  if (rc == SQLITE_OK)
172  {
173  if (pnRow == 1 && pnColumn == 2)
174  {
175  version = atol(pazResult[pnColumn+1]); // 2nd column, 2nd result row (first result row is column headers)
176  }
177  sqlite3_free_table(pazResult);
178  }
179  else
180  {
181  sqlite3_free(errmsg);
182  }
183  if (version != db_schema_version)
184  {
185  new_file = true;
186  }
187  }
188  char sql_line[512];
189  if (new_file) // remember to increment "db_schema_version" at top of file if you change the table schema
190  {
191  // remove old stuff
192  rc = sqlite3_exec(m_db,
193  "DROP TABLE IF EXISTS run; "
194  "DROP TRIGGER IF EXISTS on_selog; ", NULL, NULL, &errmsg);
195  SQLITE_CHECK_RC(rc);
196  // now re-create current tables
197  rc = sqlite3_exec(m_db,
198  "DROP TABLE IF EXISTS config;"
199  "CREATE TABLE config ("
200  "name TEXT PRIMARY KEY, "
201  "value TEXT );", NULL, NULL, &errmsg);
202  SQLITE_CHECK_RC(rc);
203  _snprintf_s(sql_line, sizeof(sql_line), _TRUNCATE, "INSERT INTO config VALUES ('version', %d);", db_schema_version);
204  rc = sqlite3_exec(m_db, sql_line, NULL, NULL, &errmsg);
205  SQLITE_CHECK_RC(rc);
206  rc = sqlite3_exec(m_db,
207  "DROP TABLE IF EXISTS selog;"
208  "CREATE TABLE selog ("
209  "run_number INTEGER NOT NULL, "
210  "source TEXT DEFAULT 'UNKNOWN', "
211  "iso_time TEXT DEFAULT '1970-01-01T00:00:00', "
212  "unix_time INTEGER DEFAULT 0, "
213  "block_name TEXT NOT NULL, "
214  "block_value TEXT NOT NULL, "
215  "is_real INTEGER DEFAULT 0);", NULL, NULL, &errmsg);
216  SQLITE_CHECK_RC(rc);
217  rc = sqlite3_exec(m_db,
218  "DROP TABLE IF EXISTS run_status;"
219  "CREATE TABLE run_status ("
220  "run_number INTEGER NOT NULL, "
221  "source TEXT, "
222  "iso_time TEXT, "
223  "unix_time INTEGER, "
224  "period INTEGER, "
225  "run_status INTEGER, "
226  "is_running INTEGER, "
227  "is_waiting INTEGER, "
228  "good_frames INTEGER, "
229  "raw_frames INTEGER, "
230  "good_uamps REAL, "
231  "raw_uamps REAL, "
232  "monitor1_sum INTEGER, "
233  "dae_beam_current REAL, "
234  "total_counts INTEGER, "
235  "count_rate REAL, "
236  "np_ratio REAL );", NULL, NULL, &errmsg);
237  SQLITE_CHECK_RC(rc);
238  rc = sqlite3_exec(m_db,
239  "DROP TABLE IF EXISTS seblocks;"
240  "CREATE TABLE seblocks ("
241  "run_number INTEGER NOT NULL, "
242  "block_name TEXT NOT NULL, "
243  "setpoint_value TEXT DEFAULT '0.0', "
244  "vi_name TEXT DEFAULT '', "
245  "read_control TEXT DEFAULT '', "
246  "set_control TEXT DEFAULT '', "
247  "button_control TEXT DEFAULT '', "
248  "options INTEGER DEFAULT 0, "
249  "nexus_name TEXT DEFAULT '', "
250  "low_limit REAL DEFAULT 0.0, "
251  "high_limit REAL DEFAULT 0.0, "
252  "units TEXT DEFAULT '', "
253  "block_value TEXT DEFAULT '0.0', "
254  "is_real INTEGER DEFAULT 0, "
255  "source TEXT DEFAULT 'UNKNOWN', "
256  "setpoint_time TEXT DEFAULT '1970-01-01T00:00:00', "
257  "value_time TEXT DEFAULT '1970-01-01T00:00:00', "
258  "PRIMARY KEY(run_number, block_name) );", NULL, NULL, &errmsg);
259  SQLITE_CHECK_RC(rc);
260  rc = sqlite3_exec(m_db,
261  "DROP TABLE IF EXISTS seperiods;"
262  "CREATE TABLE seperiods ("
263  "run_number INTEGER NOT NULL, "
264  "period INTEGER, "
265  "iso_time TEXT, "
266  "unix_time INTEGER, "
267  "is_start INTEGER, "
268  "block_name TEXT NOT NULL, "
269  "block_setpoint TEXT, "
270  "block_value TEXT );", NULL, NULL, &errmsg);
271  SQLITE_CHECK_RC(rc);
272  rc = sqlite3_exec(m_db,
273  "DROP TABLE IF EXISTS run_state;"
274  "CREATE TABLE run_state ("
275  "run_number INTEGER NOT NULL, "
276  "is_start INTEGER, "
277  "state TEXT, "
278  "iso_time TEXT, "
279  "unix_time INTEGER );", NULL, NULL, &errmsg);
280  SQLITE_CHECK_RC(rc);
281  rc = sqlite3_exec(m_db,
282  "DROP TABLE IF EXISTS measurement;"
283  "CREATE TABLE measurement ("
284  "uuid STRING PRIMARY KEY, "
285  "run_number INTEGER, "
286  "iso_time TEXT, "
287  "label STRING );", NULL, NULL, &errmsg);
288  SQLITE_CHECK_RC(rc);
289 // indexes
290  rc = sqlite3_exec(m_db,
291  "DROP INDEX IF EXISTS selog_index; "
292  "CREATE INDEX selog_index ON selog (run_number, block_name); "
293  "DROP INDEX IF EXISTS seperiods_index; "
294  "CREATE INDEX seperiods_index ON seperiods (run_number, block_name); "
295  "DROP INDEX IF EXISTS run_status_index; "
296  "CREATE INDEX run_status_index ON run_status (run_number); "
297  "DROP INDEX IF EXISTS run_state_index; "
298  "CREATE INDEX run_state_index ON run_state (run_number); ", NULL, NULL, &errmsg);
299  SQLITE_CHECK_RC(rc);
300 // views
301  rc = sqlite3_exec(m_db,
302  "DROP VIEW IF EXISTS c_period; "
303  "CREATE VIEW c_period AS SELECT value FROM config WHERE name='period'; "
304  "DROP VIEW IF EXISTS c_run_number; "
305  "CREATE VIEW c_run_number AS SELECT value FROM config WHERE name='run_number'; ",
306  NULL, NULL, &errmsg);
307  SQLITE_CHECK_RC(rc);
308 // triggers
309 // rc = sqlite3_exec(m_db,
310 // "DROP TRIGGER IF EXISTS on_selog; "
311 // "CREATE TRIGGER on_selog AFTER INSERT ON selog FOR EACH ROW BEGIN "
312 // "UPDATE seblocks SET block_value=NEW.block_value, value_time=NEW.iso_time, is_real=NEW.is_real, source=NEW.source "
313 // "WHERE block_name=NEW.block_name AND run_number=NEW.run_number; "
314 // "END; ", NULL, NULL, &errmsg);
315 // SQLITE_CHECK_RC(rc);
316  // this trigger creates entries in the new SEBLOCKS table for a new run number by
317  // copying from the old run number
318  rc = sqlite3_exec(m_db,
319  "DROP TRIGGER IF EXISTS on_config_update; "
320  "CREATE TRIGGER on_config_update AFTER UPDATE ON config FOR EACH ROW WHEN (NEW.name='run_number') BEGIN "
321  "INSERT OR IGNORE INTO seblocks SELECT NEW.value, block_name, setpoint_value, vi_name, read_control, "
322  "set_control, button_control, options, nexus_name, low_limit, high_limit, units, block_value, "
323  "is_real, source, setpoint_time, value_time FROM seblocks WHERE run_number=OLD.value; "
324  "END; ", NULL, NULL, &errmsg);
325  SQLITE_CHECK_RC(rc);
326  // this should only get called on database creation - we may then have a few values logged by SECI that have run_number=-1
327  // as get_run_number() would have failed and returned -1
328  rc = sqlite3_exec(m_db,
329  "DROP TRIGGER IF EXISTS on_config_insert; "
330  "CREATE TRIGGER on_config_insert AFTER INSERT ON config FOR EACH ROW WHEN (NEW.name='run_number') BEGIN "
331  "INSERT OR IGNORE INTO seblocks SELECT NEW.value, block_name, setpoint_value, vi_name, read_control, "
332  "set_control, button_control, options, nexus_name, low_limit, high_limit, units, block_value, "
333  "is_real, source, setpoint_time, value_time FROM seblocks WHERE run_number=-1; "
334  "END; ", NULL, NULL, &errmsg);
335  SQLITE_CHECK_RC(rc);
336  rc = sqlite3_exec(m_db,
337  "DROP TRIGGER IF EXISTS on_run_state; "
338  "CREATE TRIGGER on_run_state AFTER INSERT ON run_state FOR EACH ROW BEGIN "
339  "INSERT INTO seperiods SELECT c_run_number.value,c_period.value,NEW.iso_time,NEW.unix_time,NEW.is_start,seblocks.block_name,seblocks.setpoint_value,seblocks.block_value FROM c_run_number,c_period,seblocks WHERE seblocks.run_number=NEW.run_number; "
340  "INSERT INTO selog SELECT c_run_number.value,'TRIGGER',NEW.iso_time,NEW.unix_time,seblocks.block_name,seblocks.block_value,seblocks.is_real FROM c_run_number,seblocks WHERE seblocks.run_number=NEW.run_number; "
341  "END; ", NULL, NULL, &errmsg);
342  SQLITE_CHECK_RC(rc);
343  }
344  // create temporary tables and triggers
345  // triggers in main database cannot referecen outher databases (e.g. temp)
346  // so we need to create a temporary trigger
347  // don't think need one on period change (i.e. config table) as covered by run state change as e.g. pause change_period resume
348  if (strlen(isisdb_per_session_sql) > 0)
349  {
350  rc = sqlite3_exec(m_db, isisdb_per_session_sql, NULL, NULL, &errmsg);
351  SQLITE_CHECK_RC(rc);
352  }
353 // rc = my_sqlite3_exec(the_database, "BEGIN EXCLUSIVE TRANSACTION; VACUUM; END TRANSACTION;", NULL, NULL, &errmsg);
354 // SQLITE_CHECK_RC(rc);
355 }
356 
357 Poco::SingletonHolder<SQLITESessionPool> sqlite_session_pool;
sqlite3 * m_db
Definition: ISISDB.h:23
static const char * db_file
Definition: ISISDB.h:25
ISISDB(bool integrity_check)
Definition: ISISDB.cpp:94
Poco::SingletonHolder< SQLITESessionPool > sqlite_session_pool
Definition: ISISDB.cpp:357
~DBLock()
Definition: ISISDB.cpp:36
~ISISDB()
Definition: ISISDB.cpp:54
static const char * isisdb_per_session_sql
Definition: ISISDB.h:6
static std::list< sqlite3 * > db_list
Definition: ISISDB.h:24
static char last_error[1024]
Definition: selogger.cpp:198
DBLock()
Definition: ISISDB.cpp:20
static const int db_schema_version
update when you change database schema
Definition: ISISDB.cpp:8
const char * version()
Definition: ISISDB.cpp:47
int closeDatabase()
Definition: ISISDB.cpp:64
static volatile HANDLE db_mutex
Definition: ISISDB.cpp:17
#define SQLITE_CHECK_RC(__rc)
Definition: ISISDB.cpp:85