MIDAS
Loading...
Searching...
No Matches
history_sqlite_obsolete.cxx
Go to the documentation of this file.
1/********************************************************************\
2
3 Name: history_sqlite.cxx
4 Created by: Konstantin Olchanski
5
6 Contents: Interface class for writing MIDAS history data to SQLITE databases
7
8 $Id$
9
10\********************************************************************/
11
12#include <stdio.h>
13#include <stdint.h>
14#include <stdlib.h>
15#include <string.h>
16#include <ctype.h>
17#include <assert.h>
18#include <math.h>
19#include <errno.h>
20
21#include <vector>
22#include <string>
23#include <map>
24
25#include "mstrlcpy.h"
26
27typedef std::vector<int> IntVector;
28typedef std::vector<std::string> StringVector;
29typedef std::map<std::string, std::string> StringMap;
30typedef std::map<std::string, StringVector> StringVectorMap;
31typedef std::map<std::string, StringMap> StringMapMap;
32
33//
34// benchmarks
35//
36// /usr/bin/time ./linux/bin/mh2sql . /ladd/iris_data2/alpha/alphacpc09-elog-history/history/121019.hst
37// -rw-r--r-- 1 alpha users 161028048 Oct 19 2012 /ladd/iris_data2/alpha/alphacpc09-elog-history/history/121019.hst
38// flush 10000, sync=OFF -> 51.51user 1.51system 0:53.76elapsed 98%CPU
39// flush 1000000, sync=NORMAL -> 51.83user 2.09system 1:08.37elapsed 78%CPU (flush never activated)
40// flush 100000, sync=NORMAL -> 51.38user 1.94system 1:06.94elapsed 79%CPU
41// flush 10000, sync=NORMAL -> 51.37user 2.03system 1:31.63elapsed 58%CPU
42// flush 1000, sync=NORMAL -> 52.16user 2.70system 4:38.58elapsed 19%CPU
43
45// MIDAS includes //
47
48#include "midas.h"
49#include "history.h"
50
51#ifdef HAVE_SQLITE
52
54// helper stuff //
56
57#define FREE(x) { if (x) free(x); (x) = NULL; }
58
59static std::string TimeToString(time_t t)
60{
61 const char* sign = "";
62
63 if (t == 0)
64 return "0";
65
66 time_t tt = t;
67
68 if (t < 0) {
69 sign = "-";
70 tt = -t;
71 }
72
73 assert(tt > 0);
74
75 std::string v;
76 while (tt) {
77 char c = '0' + tt%10;
78 tt /= 10;
79 v = c + v;
80 }
81
82 v = sign + v;
83
84 //printf("time %.0f -> %s\n", (double)t, v.c_str());
85
86 return v;
87}
88
89static std::string SmallIntToString(int i)
90{
91 int ii = i;
92
93 if (i == 0)
94 return "0";
95
96 assert(i > 0);
97
98 std::string v;
99 while (i) {
100 char c = '0' + i%10;
101 i /= 10;
102 v = c + v;
103 }
104
105 printf("SmallIntToString: %d -> %s\n", ii, v.c_str());
106
107 return v;
108}
109
110static void xcmp(const std::string& x, const char* y)
111{
112 printf("->%s<-\n", y);
113 printf("=>%s<=\n", x.c_str());
114}
115
117// Definitions extracted from midas.c //
119
120/********************************************************************/
121/* data type sizes */
122static const int tid_size[] = {
123 0, /* tid == 0 not defined */
124 1, /* TID_BYTE unsigned byte 0 255 */
125 1, /* TID_SBYTE signed byte -128 127 */
126 1, /* TID_CHAR single character 0 255 */
127 2, /* TID_WORD two bytes 0 65535 */
128 2, /* TID_SHORT signed word -32768 32767 */
129 4, /* TID_DWORD four bytes 0 2^32-1 */
130 4, /* TID_INT signed dword -2^31 2^31-1 */
131 4, /* TID_BOOL four bytes bool 0 1 */
132 4, /* TID_FLOAT 4 Byte float format */
133 8, /* TID_DOUBLE 8 Byte float format */
134 4, /* TID_BITFIELD 32 Bits Bitfield 00000... 11111... */
135 0, /* TID_STRING zero terminated string */
136 0, /* TID_ARRAY variable length array of unkown type */
137 0, /* TID_STRUCT C structure */
138 0, /* TID_KEY key in online database */
139 0 /* TID_LINK link in online database */
140};
141
142/* data type names */
143static const char *tid_name[] = {
144 "NULL",
145 "BYTE",
146 "SBYTE",
147 "CHAR",
148 "WORD",
149 "SHORT",
150 "DWORD",
151 "INT",
152 "BOOL",
153 "FLOAT",
154 "DOUBLE",
155 "BITFIELD",
156 "STRING",
157 "ARRAY",
158 "STRUCT",
159 "KEY",
160 "LINK"
161};
162
163static const char *sql_type[] = {
164 "xxxINVALIDxxxNULL", // TID_NULL
165 "INTEGER", // TID_BYTE
166 "INTEGER", // TID_SBYTE
167 "TEXT", // TID_CHAR
168 "INTEGER", // TID_WORD
169 "INTEGER", // TID_SHORT
170 "INTEGER", // TID_DWORD
171 "INTEGER", // TID_INT
172 "INTEGER", // TID_BOOL
173 "REAL", // TID_FLOAT
174 "REAL", // TID_DOUBLE
175 "INTEGER", // TID_BITFIELD
176 "TEXT", // TID_STRING
177 "xxxINVALIDxxxARRAY",
178 "xxxINVALIDxxxSTRUCT",
179 "xxxINVALIDxxxKEY",
180 "xxxINVALIDxxxLINK"
181};
182
184// Handling of data types //
186
187static const char* midasTypeName(int tid)
188{
189 if (tid>=0 && tid<15)
190 return tid_name[tid];
191
192 static char buf[1024];
193 sprintf(buf, "TID_%d", tid);
194 return buf;
195}
196
197static const char* midas2sqlType(int tid)
198{
199 assert(tid>=0);
200 assert(tid<15);
201 return sql_type[tid];
202}
203
204static int sql2midasType(const char* name)
205{
206 for (int tid=0; tid<15; tid++)
207 if (strcasecmp(name, sql_type[tid])==0)
208 return tid;
209 printf("sql2midasType: Cannot convert SQL data type \'%s\' to a MIDAS data type!\n", name);
210 return 0;
211}
212
213static bool isCompatible(int tid, const char* sqlType)
214{
215 if (0)
216 printf("compare types midas \'%s\'=\'%s\' and sql \'%s\'\n", midasTypeName(tid), midas2sqlType(tid), sqlType);
217
218 if (sql2midasType(sqlType) == tid)
219 return true;
220
221 if (strcasecmp(midas2sqlType(tid), sqlType) == 0)
222 return true;
223
224 // permit writing FLOAT into DOUBLE
225 if (tid==TID_FLOAT && strcmp(sqlType, "double")==0)
226 return true;
227
228 // T2K quirk!
229 // permit writing BYTE into signed tinyint
230 if (tid==TID_BYTE && strcmp(sqlType, "tinyint")==0)
231 return true;
232
233 // T2K quirk!
234 // permit writing WORD into signed tinyint
235 if (tid==TID_WORD && strcmp(sqlType, "tinyint")==0)
236 return true;
237
238 return false;
239}
240
242// SQLITE includes //
244
245#include <sqlite3.h>
246
248// Sqlite: SQL access //
250
251typedef std::map<std::string, sqlite3*> DbMap;
252
253class Sqlite
254{
255public:
256 int fDebug;
257 bool fIsConnected;
258
259 std::string fPath;
260
261 DbMap fMap;
262
264
265 Sqlite(); // ctor
266 ~Sqlite(); // dtor
267
268 int Connect(const char* path);
269 int Disconnect();
270 bool IsConnected();
271
272 int ConnectTable(const char* table_name);
273 sqlite3* GetTable(const char* table_name);
274
275 int ListTables(std::vector<std::string> *plist);
276 int ListColumns(const char* table_name, std::vector<std::string> *plist);
277
278 int Exec(const char* table_name, const char* sql);
279 int Prepare(const char* table_name, const char* sql, sqlite3_stmt** st);
280 int Step(sqlite3_stmt* st);
281 int Finalize(sqlite3_stmt** st);
282
283 int OpenTransaction(const char* table_name);
284 int CloseTransaction(const char* table_name);
285
286 const char* GetText(sqlite3_stmt* st, int column);
288 double GetDouble(sqlite3_stmt* st, int column);
289};
290
291const char* Sqlite::GetText(sqlite3_stmt* st, int column)
292{
293 return (const char*)sqlite3_column_text(st, column);
294}
295
296int64_t Sqlite::GetInt64(sqlite3_stmt* st, int column)
297{
299}
300
301double Sqlite::GetDouble(sqlite3_stmt* st, int column)
302{
304}
305
306Sqlite::Sqlite() // ctor
307{
308 fIsConnected = false;
309 fTempDB = NULL;
310 fDebug = 0;
311}
312
313Sqlite::~Sqlite() // dtor
314{
315 Disconnect();
316}
317
318const char* xsqlite3_errstr(sqlite3* db, int errcode)
319{
320 //return sqlite3_errstr(errcode);
321 return sqlite3_errmsg(db);
322}
323
324int Sqlite::ConnectTable(const char* table_name)
325{
326 std::string fname = fPath + "/" + "mh_" + table_name + ".sqlite3";
327
328 sqlite3* db = NULL;
329
330 int status = sqlite3_open(fname.c_str(), &db);
331
332 if (status != SQLITE_OK) {
333 cm_msg(MERROR, "Sqlite::Connect", "Table %s: sqlite3_open(%s) error %d (%s)", table_name, fname.c_str(), status, xsqlite3_errstr(db, status));
335 db = NULL;
336 return DB_FILE_ERROR;
337 }
338
339#if SQLITE_VERSION_NUMBER >= 3006020
341 if (status != SQLITE_OK) {
342 cm_msg(MERROR, "Sqlite::Connect", "Table %s: sqlite3_extended_result_codes(1) error %d (%s)", table_name, status, xsqlite3_errstr(db, status));
343 }
344#else
345#warning Missing sqlite3_extended_result_codes()!
346#endif
347
348 fMap[table_name] = db;
349
350 Exec(table_name, "PRAGMA journal_mode=persist;");
351 Exec(table_name, "PRAGMA synchronous=normal;");
352 //Exec(table_name, "PRAGMA synchronous=off;");
353 Exec(table_name, "PRAGMA journal_size_limit=-1;");
354
355 if (0) {
356 Exec(table_name, "PRAGMA legacy_file_format;");
357 Exec(table_name, "PRAGMA synchronous;");
358 Exec(table_name, "PRAGMA journal_mode;");
359 Exec(table_name, "PRAGMA journal_size_limit;");
360 }
361
362 if (fDebug)
363 cm_msg(MINFO, "Sqlite::Connect", "Table %s: connected to Sqlite file \'%s\'", table_name, fname.c_str());
364
365 return DB_SUCCESS;
366}
367
368sqlite3* Sqlite::GetTable(const char* table_name)
369{
370 sqlite3* db = fMap[table_name];
371
372 if (db)
373 return db;
374
375 int status = ConnectTable(table_name);
376 if (status != DB_SUCCESS)
377 return NULL;
378
379 return fMap[table_name];
380}
381
382int Sqlite::Connect(const char* path)
383{
384 if (fIsConnected)
385 Disconnect();
386
387 fPath = path;
388
389 if (fDebug)
390 cm_msg(MINFO, "Sqlite::Connect", "Connected to Sqlite database in \'%s\'", path);
391
392 fIsConnected = true;
393
394 return DB_SUCCESS;
395}
396
397int Sqlite::Disconnect()
398{
399 if (!fIsConnected)
400 return DB_SUCCESS;
401
402 for (DbMap::iterator iter = fMap.begin(); iter != fMap.end(); ++iter) {
403 const char* table_name = iter->first.c_str();
404 sqlite3* db = iter->second;
405 int status = sqlite3_close(db);
406 if (status != SQLITE_OK) {
407 cm_msg(MERROR, "Sqlite::Disconnect", "sqlite3_close(%s) error %d (%s)", table_name, status, xsqlite3_errstr(db, status));
408 }
409 }
410
411 fMap.clear();
412
413 fIsConnected = false;
414
415 return DB_SUCCESS;
416}
417
418bool Sqlite::IsConnected()
419{
420 return fIsConnected;
421}
422
423int Sqlite::OpenTransaction(const char* table_name)
424{
425 int status = Exec(table_name, "BEGIN TRANSACTION");
426 return status;
427}
428
429int Sqlite::CloseTransaction(const char* table_name)
430{
431 int status = Exec(table_name, "COMMIT TRANSACTION");
432 return status;
433}
434
435int Sqlite::Prepare(const char* table_name, const char* sql, sqlite3_stmt** st)
436{
437 sqlite3* db = GetTable(table_name);
438 if (!db)
439 return DB_FILE_ERROR;
440
441 if (fDebug)
442 printf("Sqlite::Prepare(%s, %s)\n", table_name, sql);
443
444 assert(fTempDB==NULL);
445 fTempDB = db;
446
447#if SQLITE_VERSION_NUMBER >= 3006020
448 int status = sqlite3_prepare_v2(db, sql, strlen(sql), st, NULL);
449#else
450#warning Missing sqlite3_prepare_v2()!
451 int status = sqlite3_prepare(db, sql, strlen(sql), st, NULL);
452#endif
453
454 if (status == SQLITE_OK)
455 return DB_SUCCESS;
456
457 std::string sqlstring = sql;
458 cm_msg(MERROR, "Sqlite::Prepare", "Table %s: sqlite3_prepare_v2(%s...) error %d (%s)", table_name, sqlstring.substr(0,60).c_str(), status, xsqlite3_errstr(db, status));
459
460 fTempDB = NULL;
461
462 return DB_FILE_ERROR;
463}
464
465int Sqlite::Step(sqlite3_stmt* st)
466{
467 if (0 && fDebug)
468 printf("Sqlite::Step()\n");
469
470 assert(fTempDB);
471
472 int status = sqlite3_step(st);
473
474 if (status == SQLITE_DONE)
475 return DB_NO_MORE_SUBKEYS;
476
477 if (status == SQLITE_ROW)
478 return DB_SUCCESS;
479
480 cm_msg(MERROR, "Sqlite::Step", "sqlite3_step() error %d (%s)", status, xsqlite3_errstr(fTempDB, status));
481
482 return DB_FILE_ERROR;
483}
484
485int Sqlite::Finalize(sqlite3_stmt** st)
486{
487 if (0 && fDebug)
488 printf("Sqlite::Finalize()\n");
489
490 assert(fTempDB);
491
492 int status = sqlite3_finalize(*st);
493
494 if (status != SQLITE_OK) {
495 cm_msg(MERROR, "Sqlite::Finalize", "sqlite3_finalize() error %d (%s)", status, xsqlite3_errstr(fTempDB, status));
496
497 fTempDB = NULL;
498 st = NULL; // FIXME: maybe a memory leak?
499 return DB_FILE_ERROR;
500 }
501
502 fTempDB = NULL;
503
504 st = NULL;
505 return DB_SUCCESS;
506}
507
508#include <dirent.h>
509
510int Sqlite::ListTables(std::vector<std::string> *plist)
511{
512 if (!fIsConnected)
513 return DB_FILE_ERROR;
514
515 if (fDebug)
516 printf("Sqlite::ListTables at path [%s]\n", fPath.c_str());
517
518 int status;
519
520 const char* cmd = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;";
521
522 DIR *dir = opendir(fPath.c_str());
523 if (!dir) {
524 cm_msg(MERROR, "Sqlite::ListTables", "Cannot opendir(%s), errno %d (%s)", fPath.c_str(), errno, strerror(errno));
525 return HS_FILE_ERROR;
526 }
527
528 while (1) {
529 const struct dirent* de = readdir(dir);
530 if (!de)
531 break;
532
533 const char* dn = de->d_name;
534
535 //if (dn[0]!='m' || dn[1]!='h')
536 //continue;
537
538 const char* s;
539
540 s = strstr(dn, "mh_");
541 if (!s || s!=dn)
542 continue;
543
544 s = strstr(dn, ".sqlite3");
545 if (!s || s[8]!=0)
546 continue;
547
548 char table_name[256];
549 mstrlcpy(table_name, dn+3, sizeof(table_name));
550 char* ss = strstr(table_name, ".sqlite3");
551 if (!ss)
552 continue;
553 *ss = 0;
554
555 //printf("dn [%s] tn [%s]\n", dn, table_name);
556
558
559 status = Prepare(table_name, cmd, &st);
560 if (status != DB_SUCCESS)
561 continue;
562
563 while (1) {
564 status = Step(st);
565 if (status != DB_SUCCESS)
566 break;
567
568 const char* tablename = GetText(st, 0);
569 //printf("table [%s]\n", tablename);
570 plist->push_back(tablename);
571 }
572
573 status = Finalize(&st);
574 }
575
576 closedir(dir);
577 dir = NULL;
578
579 return DB_SUCCESS;
580}
581
582int Sqlite::ListColumns(const char* table, std::vector<std::string> *plist)
583{
584 if (!fIsConnected)
585 return DB_FILE_ERROR;
586
587 if (fDebug)
588 printf("Sqlite::ListColumns for table \'%s\'\n", table);
589
590 std::string cmd;
591 cmd = "PRAGMA table_info(";
592 cmd += table;
593 cmd += ");";
594
595 int status;
596
597 //status = Exec(cmd);
598
600
601 status = Prepare(table, cmd.c_str(), &st);
602 if (status != DB_SUCCESS)
603 return status;
604
605 while (1) {
606 status = Step(st);
607 if (status != DB_SUCCESS)
608 break;
609
610 const char* colname = GetText(st, 1);
611 const char* coltype = GetText(st, 2);
612 //printf("column [%s] [%s]\n", colname, coltype);
613 plist->push_back(colname); // column name
614 plist->push_back(coltype); // column type
615 }
616
617 status = Finalize(&st);
618
619 return DB_SUCCESS;
620}
621
622static int callback_debug = 0;
623
624static int callback(void *NotUsed, int argc, char **argv, char **azColName){
625 if (callback_debug) {
626 printf("history_sqlite::callback---->\n");
627 for (int i=0; i<argc; i++){
628 printf("history_sqlite::callback[%d] %s = %s\n", i, azColName[i], argv[i] ? argv[i] : "NULL");
629 }
630 }
631 return 0;
632}
633
634int Sqlite::Exec(const char* table_name, const char* sql)
635{
636 // return values:
637 // DB_SUCCESS
638 // DB_FILE_ERROR: not connected
639 // DB_NO_KEY: "table not found"
640
641 if (!fIsConnected)
642 return DB_FILE_ERROR;
643
644 sqlite3* db = GetTable(table_name);
645 if (!db)
646 return DB_FILE_ERROR;
647
648 if (fDebug)
649 printf("Sqlite::Exec(%s, %s)\n", table_name, sql);
650
651 int status;
652
653 callback_debug = fDebug;
654 char* errmsg = NULL;
655
656 status = sqlite3_exec(db, sql, callback, 0, &errmsg);
657 if (status != SQLITE_OK) {
658 std::string sqlstring = sql;
659 cm_msg(MERROR, "Sqlite::Exec", "Table %s: sqlite3_exec(%s...) error %d (%s)", table_name, sqlstring.substr(0,60).c_str(), status, errmsg);
661 return DB_FILE_ERROR;
662 }
663
664 return DB_SUCCESS;
665}
666
668// Done with SQL stuff //
670
672// Data structures to keep track of Events and Tags //
674
675struct Tag
676{
677 std::string column_name;
678 int offset;
679 TAG tag;
680 bool create;
681};
682
683struct Event
684{
685 std::string event_name;
686 std::string table_name;
687 std::vector<Tag> tags;
688 bool active;
690
691 Event() // ctor
692 {
693 active = false;
695 }
696
697 ~Event() // dtor
698 {
699 active = false;
700 assert(transactionCount == 0);
701 }
702};
703
704static void PrintTags(int ntags, const TAG tags[])
705{
706 for (int i=0; i<ntags; i++)
707 printf("tag %d: %s %s[%d]\n", i, midasTypeName(tags[i].type), tags[i].name, tags[i].n_data);
708}
709
710int WriteEvent(Sqlite* sql, Event *e, time_t t, const char*buf, int size)
711{
712 //printf("event %d, time %s", rec.event_id, ctime(&t));
713
714 const char* table_name = e->table_name.c_str();
715
716 int n = e->tags.size();
717
718 std::string tags;
719 std::string values;
720
721 //if (n>0)
722 // printf(" %s", ctime(&t));
723
724 for (int i=0; i<n; i++) {
725 const Tag*t = &e->tags[i];
726
727 if (t) {
728 int offset = t->offset;
729 void* ptr = (void*)(buf+offset);
730
731 int arraySize = t->tag.n_data;
732
733 for (int j=0; j<arraySize; j++) {
734 tags += ", ";
735 values += ", ";
736
737 if (arraySize <= 1)
738 tags += "\'" + t->column_name + "\'";
739 else {
740 tags += "\'" + t->column_name;
741 char s[256];
742 sprintf(s,"_%d", j);
743 tags += s;
744 tags += "\'";
745 }
746
747 char s[1024];
748
749 switch (t->tag.type) {
750 default:
751 sprintf(s, "unknownType%d", t->tag.type);
752 break;
753 case 1: /* BYTE */
754 sprintf(s, "%u",((uint8_t*)ptr)[j]);
755 break;
756 case 2: /* SBYTE */
757 sprintf(s, "%d",((int8_t*)ptr)[j]);
758 break;
759 case 3: /* CHAR */
760 sprintf(s, "\'%c\'",((char*)ptr)[j]);
761 break;
762 case 4: /* WORD */
763 sprintf(s, "%u",((uint16_t*)ptr)[j]);
764 break;
765 case 5: /* SHORT */
766 sprintf(s, "%d",((int16_t*)ptr)[j]);
767 break;
768 case 6: /* DWORD */
769 sprintf(s, "%u",((uint32_t*)ptr)[j]);
770 break;
771 case 7: /* INT */
772 sprintf(s, "%d",((int32_t*)ptr)[j]);
773 break;
774 case 8: /* BOOL */
775 sprintf(s, "%u",((uint32_t*)ptr)[j]);
776 break;
777 case 9: /* FLOAT */
778 sprintf(s, "\'%.8g\'",((float*)ptr)[j]);
779 break;
780 case 10: /* DOUBLE */
781 sprintf(s, "\'%.16g\'",((double*)ptr)[j]);
782 break;
783 }
784
785 values += s;
786 }
787 }
788 }
789
790 // 2001-02-16 20:38:40.1
791 struct tm tms;
792 localtime_r(&t, &tms); // somebody must call tzset() before this!
793 char s[1024];
794 strftime(s,sizeof(s)-1,"%Y-%m-%d %H:%M:%S.0",&tms);
795
796 std::string cmd;
797 cmd = "INSERT INTO \'";
798 cmd += table_name;
799 cmd += "\' (_t_time, _i_time";
800 cmd += tags;
801 cmd += ") VALUES (\'";
802 cmd += s;
803 cmd += "\', \'";
804 cmd += TimeToString(t);
805 cmd += "\'";
806 cmd += values;
807 cmd += ");";
808
809 if (e->transactionCount == 0)
810 sql->OpenTransaction(table_name);
811
812 e->transactionCount++;
813
814 int status = sql->Exec(table_name, cmd.c_str());
815
816 if (e->transactionCount > 100000) {
817 //printf("flush table %s\n", table_name);
818 sql->CloseTransaction(table_name);
819 e->transactionCount = 0;
820 }
821
822 if (status != DB_SUCCESS) {
823 return status;
824 }
825
826 return HS_SUCCESS;
827}
828
829// convert MIDAS names to SQL names
830
831static std::string MidasNameToSqlName(const char* s)
832{
833 std::string out;
834
835 for (int i=0; s[i]!=0; i++) {
836 char c = s[i];
837 if (isalpha(c) || isdigit(c))
838 out += tolower(c);
839 else
840 out += '_';
841 }
842
843 return out;
844}
845
847// Implementation of the MidasHistoryInterface //
849
851{
852public:
853 Sqlite *fSql;
854 int fDebug;
855 std::string fConnectString;
856 std::vector<Event*> fEvents;
857
859 {
860 fDebug = 0;
861 fSql = new Sqlite();
863 }
864
866 {
868 delete fSql;
869 fSql = NULL;
870 }
871
872 int hs_set_debug(int debug)
873 {
874 int old = fDebug;
875 fDebug = debug;
876 fSql->fDebug = debug;
877 return old;
878 }
879
880 int hs_connect(const char* connect_string)
881 {
882 if (fDebug)
883 printf("hs_connect [%s]!\n", connect_string);
884
885 assert(fSql);
886
887 if (fSql->IsConnected())
888 if (strcmp(fConnectString.c_str(), connect_string) == 0)
889 return HS_SUCCESS;
890
892
893 if (!connect_string || strlen(connect_string) < 1) {
894 // FIXME: should use "logger dir" or some such default, that code should be in hs_get_history(), not here
895 connect_string = ".";
896 }
897
899
900 if (fDebug)
901 printf("hs_connect: connecting to SQL database \'%s\'\n", fConnectString.c_str());
902
903 int status = fSql->Connect(fConnectString.c_str());
904 if (status != DB_SUCCESS)
905 return status;
906
907 return HS_SUCCESS;
908 }
909
910 int hs_disconnect()
911 {
912 if (fDebug)
913 printf("hs_disconnect!\n");
914
916
917 fSql->Disconnect();
918
920
921 return HS_SUCCESS;
922 }
923
924 std::string GetEventName(const char* table_name)
925 {
926 int status;
927
928 std::string cmd;
929 cmd = "SELECT event_name, _i_time FROM \'_event_name_";
930 cmd += table_name;
931 cmd += "\' WHERE table_name='";
932 cmd += table_name;
933 cmd += "' ORDER BY _i_time ASC;";
934
936
937 status = fSql->Prepare(table_name, cmd.c_str(), &st);
938
939 if (status != DB_SUCCESS) {
940 return table_name;
941 }
942
943 std::string xevent_name;
945
946 while (1) {
947 status = fSql->Step(st);
948
949 if (status != DB_SUCCESS)
950 break;
951
952 // NOTE: SQL "SELECT ORDER BY _i_time ASC" returns data sorted by time
953 // in this code we use the data from the last data row
954 // so if multiple rows are present, the latest one is used
955
956 xevent_name = fSql->GetText(st, 0);
957 xevent_time = fSql->GetInt64(st, 1);
958
959 //printf("read event name [%s] time %d\n", xevent_name.c_str(), (int)xevent_time);
960 }
961
962 status = fSql->Finalize(&st);
963
964 return xevent_name;
965 }
966
967 std::string GetTableName(const char* event_name)
968 {
969 std::vector<std::string> tables;
970 int status = fSql->ListTables(&tables);
971 if (status != DB_SUCCESS)
972 return "";
973
974 for (unsigned i=0; i<tables.size(); i++) {
975 const char* tt = tables[i].c_str();
976
977 const char *s = strstr(tt, "_event_name");
978 if (!s || s!=tt)
979 continue;
980
981 const char* tn = tt + 12;
982 //printf("looking for event name [%s] maybe [%s], found [%s] [%s]\n", event_name, maybe_table_name, tt, tn);
983
984 std::string xevent_name = GetEventName(tn);
985
986 if (strcmp(xevent_name.c_str(), event_name) == 0) {
987 //printf("table name for event [%s] is [%s]\n", event_name, tn);
988 //assert(!"here!");
989 return tn;
990 }
991 }
992
993 return "";
994 }
995
996 int GetColumnNames(const char* table_name, StringMap *ptag2col, StringMap *pcol2tag)
997 {
998 std::string cmd;
999 cmd = "SELECT column_name, tag_name, _i_time FROM \'_column_names_";
1000 cmd += table_name;
1001 cmd += "\' WHERE table_name='";
1002 cmd += table_name;
1003 cmd += "' ORDER BY _i_time ASC;";
1004
1006
1007 int status = fSql->Prepare(table_name, cmd.c_str(), &st);
1008
1009 if (status != DB_SUCCESS) {
1010 return status;
1011 }
1012
1013 while (1) {
1014 status = fSql->Step(st);
1015
1016 if (status != DB_SUCCESS)
1017 break;
1018
1019 // NOTE: SQL "SELECT ORDER BY _i_time ASC" returns data sorted by time
1020 // in this code we use the data from the last data row
1021 // so if multiple rows are present, the latest one is used
1022
1023 std::string col_name = fSql->GetText(st, 0);
1024 std::string tag_name = fSql->GetText(st, 1);
1025 time_t xxx_time = fSql->GetInt64(st, 2);
1026
1027 if (ptag2col)
1028 (*ptag2col)[tag_name] = col_name;
1029
1030 if (pcol2tag)
1031 (*pcol2tag)[col_name] = tag_name;
1032
1033 if (fDebug>1)
1034 printf("read table [%s] column [%s] tag name [%s] time %d\n", table_name, col_name.c_str(), tag_name.c_str(), (int)xxx_time);
1035 }
1036
1037 status = fSql->Finalize(&st);
1038
1039 return HS_SUCCESS;
1040 }
1041
1043 // Functions used by mlogger //
1045
1046 int hs_define_event(const char* event_name, int ntags, const TAG tags[])
1047 {
1048 int status;
1049
1050 if (fDebug) {
1051 printf("hs_define_event: event name [%s] with %d tags\n", event_name, ntags);
1052 if (fDebug > 1)
1053 PrintTags(ntags, tags);
1054 }
1055
1056 // delete all events with the same name
1057 for (unsigned int i=0; i<fEvents.size(); i++)
1058 if (fEvents[i])
1059 if (fEvents[i]->event_name == event_name) {
1060 if (fDebug)
1061 printf("deleting exising event %s\n", event_name);
1062
1063 if (fEvents[i]->transactionCount > 0) {
1064 status = fSql->CloseTransaction(fEvents[i]->table_name.c_str());
1065 fEvents[i]->transactionCount = 0;
1066 }
1067
1068 delete fEvents[i];
1069 fEvents[i] = NULL;
1070 }
1071
1072 // check for duplicate tag names
1073 for (int i=0; i<ntags; i++) {
1074 for (int j=i+1; j<ntags; j++) {
1075 if (strcmp(tags[i].name, tags[j].name) == 0) {
1076 cm_msg(MERROR, "hs_define_event", "Error: History event \'%s\' has duplicate tag name \'%s\' at indices %d and %d", event_name, tags[i].name, i, j);
1077 return HS_FILE_ERROR;
1078 }
1079 }
1080 }
1081
1082 Event* e = new Event();
1083
1084 e->active = true;
1085 e->event_name = event_name;
1086
1087 std::string table_name = GetTableName(event_name);
1088
1089 if (table_name.length() < 1) {
1090 table_name = MidasNameToSqlName(event_name);
1091 }
1092
1093 e->table_name = table_name;
1094
1095 std::vector<std::string> columns;
1096
1097 status = fSql->ListColumns(e->table_name.c_str(), &columns);
1098 if (status != DB_SUCCESS)
1099 return status;
1100
1102
1103 if (columns.size() > 0) {
1104 status = GetColumnNames(table_name.c_str(), &colnames, NULL);
1105 if (status != HS_SUCCESS)
1106 return HS_FILE_ERROR;
1107 }
1108
1109 double now = time(NULL);
1110
1111 status = fSql->OpenTransaction(e->table_name.c_str());
1112 if (status != DB_SUCCESS)
1113 return HS_FILE_ERROR;
1114
1115 if (columns.size() <= 0) {
1116 // SQL table does not exist
1117
1118 std::string cmd;
1119
1120 cmd = "CREATE TABLE \'";
1121 cmd += e->table_name;
1122 cmd += "\' (_t_time TIMESTAMP NOT NULL, _i_time INTEGER NOT NULL);";
1123
1124 status = fSql->Exec(e->table_name.c_str(), cmd.c_str());
1125
1126 cmd = "CREATE INDEX \'";
1127 cmd += e->table_name;
1128 cmd += "_i_time_index\' ON \'";
1129 cmd += e->table_name;
1130 cmd += "\' (_i_time ASC);";
1131
1132 status = fSql->Exec(e->table_name.c_str(), cmd.c_str());
1133
1134 cmd = "CREATE INDEX \'";
1135 cmd += e->table_name;
1136 cmd += "_t_time_index\' ON \'";
1137 cmd += e->table_name;
1138 cmd += "\' (_t_time);";
1139
1140 status = fSql->Exec(e->table_name.c_str(), cmd.c_str());
1141
1142 cmd = "CREATE TABLE \'_event_name_";
1143 cmd += e->table_name;
1144 cmd += "\' (table_name TEXT NOT NULL, event_name TEXT NOT NULL, _i_time INTEGER NOT NULL);";
1145
1146 status = fSql->Exec(e->table_name.c_str(), cmd.c_str());
1147
1148 cmd = "INSERT INTO \'_event_name_";
1149 cmd += e->table_name;
1150 cmd += "\' (table_name, event_name, _i_time) VALUES (\'";
1151 cmd += e->table_name;
1152 cmd += "\', \'";
1153 cmd += e->event_name;
1154 cmd += "\', \'";
1155 cmd += TimeToString(now);
1156 cmd += "\');";
1157
1158 status = fSql->Exec(e->table_name.c_str(), cmd.c_str());
1159
1160 cmd = "CREATE TABLE \'_column_names_";
1161 cmd += e->table_name;
1162 cmd += "\' (table_name TEXT NOT NULL, column_name TEXT NOT NULL, tag_name TEXT NOT NULL, tag_type TEXT NOT NULL, column_type TEXT NOT NULL, _i_time INTEGER NOT NULL);";
1163
1164 status = fSql->Exec(e->table_name.c_str(), cmd.c_str());
1165 }
1166
1167 int offset = 0;
1168 for (int i=0; i<ntags; i++) {
1169 for (unsigned int j=0; j<tags[i].n_data; j++) {
1170 int tagtype = tags[i].type;
1171 std::string tagname = tags[i].name;
1172 std::string maybe_colname = MidasNameToSqlName(tags[i].name);
1173
1174 if (tags[i].n_data > 1) {
1175 char s[256];
1176 sprintf(s, "[%d]", j);
1177 tagname += s;
1178
1179 sprintf(s, "_%d", j);
1180 maybe_colname += s;
1181 }
1182
1183 std::string colname = colnames[tagname];
1184
1185 if (colname.length() < 1) {
1186 // no column name entry for this tag name
1188 }
1189
1190 // check for duplicate column names and for incompatible column data type
1191
1192 while (1) {
1193 bool dupe = false;
1194
1195 for (unsigned k=0; k<e->tags.size(); k++) {
1196 if (colname == e->tags[k].column_name) {
1197 printf("hs_define_event: event [%s] tag [%s] duplicate column name [%s] with tag [%s]\n", event_name, tagname.c_str(), colname.c_str(), e->tags[k].tag.name);
1198 dupe = true;
1199 break;
1200 }
1201 }
1202
1203 // if duplicate column name, change it, try again
1204 if (dupe) {
1205 char s[256];
1206 sprintf(s, "_%d", rand());
1207 colname += s;
1208 continue;
1209 }
1210
1211 bool compatible = true;
1212
1213 for (size_t k=0; k<columns.size(); k+=2) {
1214 if (colname == columns[k]) {
1215 // column already exists, check it's data type
1216
1218
1219 //printf("column \'%s\', data type %s\n", colname.c_str(), columns[k+1].c_str());
1220
1221 //printf("hs_define_event: event [%s] tag [%s] type %d (%s), column [%s] type [%s] compatible %d\n", event_name, tagname.c_str(), tagtype, midasTypeName(tagtype), colname.c_str(), columns[k+1].c_str(), compatible);
1222
1223 break;
1224 }
1225 }
1226
1227 // if incompatible column data type, change column name, try again
1228 if (!compatible) {
1229 char s[256];
1230 sprintf(s, "_%d", rand());
1231 colname += s;
1232 continue;
1233 }
1234
1235 break;
1236 }
1237
1238 std::string coltype = midas2sqlType(tagtype);
1239
1240 // if column name had changed (or is not listed in _column_names), add it
1241 if (colname != colnames[tagname]) {
1242 std::string cmd;
1243 cmd = "INSERT INTO \'_column_names_";
1244 cmd += e->table_name;
1245 cmd += "\' (table_name, column_name, tag_name, tag_type, column_type, _i_time) VALUES (\'";
1246 cmd += e->table_name;
1247 cmd += "\', \'";
1248 cmd += colname;
1249 cmd += "\', \'";
1250 cmd += tagname;
1251 cmd += "\', \'";
1252 cmd += midasTypeName(tagtype);
1253 cmd += "\', \'";
1254 cmd += coltype;
1255 cmd += "\', \'";
1256 cmd += TimeToString(now);
1257 cmd += "\');";
1258 status = fSql->Exec(e->table_name.c_str(), cmd.c_str());
1259 }
1260
1261 // if SQL column does not exist, create it
1262
1263 bool column_exists = false;
1264 for (size_t k=0; k<columns.size(); k+=2) {
1265 if (colname == columns[k]) {
1266 column_exists = true;
1267 break;
1268 }
1269 }
1270
1271 if (!column_exists) {
1272 std::string cmd;
1273 cmd = "ALTER TABLE \'";
1274 cmd += e->table_name;
1275 cmd += "\' ADD COLUMN \'";
1276 cmd += colname;
1277 cmd += "\' ";
1278 cmd += coltype;
1279 cmd += ";";
1280
1281 status = fSql->Exec(e->table_name.c_str(), cmd.c_str());
1282
1283 if (status != DB_SUCCESS) {
1284 e->active = false;
1285 return HS_FILE_ERROR;
1286 }
1287 }
1288
1289 Tag t;
1290 t.column_name = colname;
1291 t.offset = offset;
1292 t.tag = tags[i];
1293 t.tag.n_data = 1;
1294 e->tags.push_back(t);
1295 int size = tid_size[tags[i].type];
1296 offset += size;
1297 }
1298 }
1299
1300 status = fSql->CloseTransaction(e->table_name.c_str());
1301 if (status != DB_SUCCESS) {
1302 return HS_FILE_ERROR;
1303 }
1304
1305 // find empty slot in events list
1306 for (unsigned int i=0; i<fEvents.size(); i++)
1307 if (!fEvents[i]) {
1308 fEvents[i] = e;
1309 e = NULL;
1310 break;
1311 }
1312
1313 // if no empty slots, add at the end
1314 if (e)
1315 fEvents.push_back(e);
1316
1317 return HS_SUCCESS;
1318 }
1319
1320 int hs_write_event(const char* event_name, time_t timestamp, int buffer_size, const char* buffer)
1321 {
1322 if (fDebug)
1323 printf("hs_write_event: write event \'%s\', time %d, size %d\n", event_name, (int)timestamp, buffer_size);
1324
1325 Event *e = NULL;
1326
1327 // find this event
1328 for (size_t i=0; i<fEvents.size(); i++)
1329 if (fEvents[i]->event_name == event_name) {
1330 e = fEvents[i];
1331 break;
1332 }
1333
1334 // not found
1335 if (!e)
1336 return HS_UNDEFINED_EVENT;
1337
1338 // deactivated because of error?
1339 if (!e->active)
1340 return HS_FILE_ERROR;
1341
1342 int status = WriteEvent(fSql, e, timestamp, buffer, buffer_size);
1343
1344 // if could not write to SQL?
1345 if (status != HS_SUCCESS) {
1346 // otherwise, deactivate this event
1347
1348 e->active = false;
1349
1350 cm_msg(MERROR, "hs_write_event", "Event \'%s\' disabled after write error %d into SQL database \'%s\'", event_name, status, fConnectString.c_str());
1351
1352 return HS_FILE_ERROR;
1353 }
1354
1355 return HS_SUCCESS;
1356 }
1357
1358 int hs_flush_buffers()
1359 {
1360 int status = HS_SUCCESS;
1361
1362 if (fDebug)
1363 printf("hs_flush_buffers!\n");
1364
1365 for (unsigned int i=0; i<fEvents.size(); i++)
1366 if (fEvents[i])
1367 if (fEvents[i]->transactionCount > 0) {
1368 int xstatus = fSql->CloseTransaction(fEvents[i]->table_name.c_str());
1369 if (xstatus != HS_SUCCESS)
1370 status = xstatus;
1371 fEvents[i]->transactionCount = 0;
1372 }
1373
1374 return status;
1375 }
1376
1378 // Functions used by mhttpd //
1380
1381 std::vector<std::string> fEventsCache;
1382 std::vector<std::string> fTablesCache;
1386
1387 int hs_clear_cache()
1388 {
1389 if (fDebug)
1390 printf("hs_clear_cache!\n");
1391
1392 fTablesCache.clear();
1393 fEventsCache.clear();
1394 fTableNamesCache.clear();
1395 fColumnsCache.clear();
1396 fColumnNamesCache.clear();
1397
1398 return HS_SUCCESS;
1399 }
1400
1401 void ReadTablesCache()
1402 {
1403 if (fDebug)
1404 printf("ReadTablesCache!\n");
1405
1406 fTablesCache.clear();
1407
1409 }
1410
1411 void ReadEventsCache()
1412 {
1413 if (fDebug)
1414 printf("ReadEventsCache!\n");
1415
1416 if (fTablesCache.size() == 0)
1418
1419 for (unsigned i=0; i<fTablesCache.size(); i++) {
1420 const char* tn = fTablesCache[i].c_str();
1421
1422 const char* s;
1423 s = strstr(tn, "_event_name_");
1424 if (s == tn)
1425 continue;
1426 s = strstr(tn, "_column_names_");
1427 if (s == tn)
1428 continue;
1429
1430 std::string en = GetEventName(tn);
1431
1432 bool dupe = false;
1433 for (unsigned j=0; j<fEventsCache.size(); j++)
1434 if (fEventsCache[j] == en) {
1435 dupe = true;
1436 break;
1437 }
1438
1440
1441 if (!dupe)
1442 fEventsCache.push_back(en);
1443 }
1444 }
1445
1446 void ReadColumnsCache(const char* table_name)
1447 {
1448 fColumnsCache[table_name].clear();
1449 fSql->ListColumns(table_name, &fColumnsCache[table_name]);
1450
1451 fColumnNamesCache[table_name].clear();
1452
1453 // assign default column name same as SQL column name
1454 for (unsigned i=0; i<fColumnsCache[table_name].size(); i+=2) {
1455 const std::string cn = fColumnsCache[table_name][i];
1456 fColumnNamesCache[table_name][cn] = cn;
1457 }
1458
1459 // read column names from SQL
1460 GetColumnNames(table_name, NULL, &fColumnNamesCache[table_name]);
1461 }
1462
1463 int hs_get_events(std::vector<std::string> *pevents)
1464 {
1465 if (fDebug)
1466 printf("hs_get_events!\n");
1467
1468 if (fEventsCache.size() == 0) {
1470 }
1471
1472 assert(pevents);
1473 *pevents = fEventsCache;
1474
1475 return HS_SUCCESS;
1476 }
1477
1478 int hs_get_tags(const char* event_name, std::vector<TAG> *ptags)
1479 {
1480 if (fDebug)
1481 printf("hs_get_tags for [%s]\n", event_name);
1482
1483 assert(ptags);
1484
1485 if (fEventsCache.size() == 0)
1487
1488 bool not_found = true;
1489 for (unsigned i=0; i<fTablesCache.size(); i++) {
1490 const std::string tn = fTablesCache[i].c_str();
1491 const char* en = fTableNamesCache[tn].c_str();
1492
1493 if (strcmp(tn.c_str(), event_name) != 0) // match table name?
1494 if (strcmp(en, event_name) != 0) // match event name?
1495 continue;
1496 not_found = false;
1497
1498 if (fColumnsCache[tn].size() == 0)
1499 ReadColumnsCache(tn.c_str());
1500
1501 for (unsigned int j=0; j<fColumnsCache[tn].size(); j+=2) {
1502 const std::string cn = fColumnsCache[tn][j];
1503 const std::string ct = fColumnsCache[tn][j+1];
1504 if (cn == "_t_time")
1505 continue;
1506 if (cn == "_i_time")
1507 continue;
1508
1509 const char* tagname = fColumnNamesCache[tn][cn].c_str();
1510
1511 //printf("event_name [%s], table_name [%s], column name [%s], tag name [%s]\n", event_name, tn.c_str(), cn.c_str(), tagname);
1512
1513 if (strlen(tagname) < 1)
1514 tagname = cn.c_str();
1515
1516 bool dupe = false;
1517
1518 for (unsigned k=0; k<ptags->size(); k++)
1519 if (strcmp((*ptags)[k].name, tagname) == 0) {
1520 dupe = true;
1521 break;
1522 }
1523
1524 if (!dupe) {
1525 TAG t;
1526 mstrlcpy(t.name, tagname, sizeof(t.name));
1527 t.type = sql2midasType(ct.c_str());
1528 t.n_data = 1;
1529
1530 ptags->push_back(t);
1531 }
1532 }
1533 }
1534
1535 if (fDebug) {
1536 printf("hs_get_tags: %d tags\n", (int)ptags->size());
1537 for (unsigned i=0; i<ptags->size(); i++) {
1538 printf(" tag[%d]: %s[%d] type %d\n", i, (*ptags)[i].name, (*ptags)[i].n_data, (*ptags)[i].type);
1539 }
1540 }
1541
1542 if (not_found)
1543 return HS_UNDEFINED_EVENT;
1544
1545 return HS_SUCCESS;
1546 }
1547
1548 /*------------------------------------------------------------------*/
1549
1550 struct XItem {
1551 XItem(const char* tn, const char* cn) : tableName(tn), columnName(cn) { }; // ctor
1552
1553 std::string tableName;
1554 std::string columnName;
1555 };
1556
1557 typedef std::vector<XItem> XItemVector;
1558
1559 int FindItem(const char* const event_name, const char* const tag_name, int tag_index, XItemVector* result)
1560 {
1561 if (fEventsCache.size() == 0)
1563
1564 // new-style event name: "equipment_name/variable_name:tag_name"
1565 // old-style event name: "equipment_name:tag_name" ("variable_name" is missing)
1566 bool newStyleEventName = (strchr(event_name, '/')!=NULL);
1567
1568 for (unsigned i=0; i<fTablesCache.size(); i++) {
1569 const char* tn = fTablesCache[i].c_str();
1570 const char* en = fTableNamesCache[tn].c_str();
1571 if (strlen(en) < 1)
1572 en = tn;
1573
1574 //printf("looking for event_name [%s], try table [%s] event name [%s], new style [%d]\n", event_name, tn, en, newStyleEventName);
1575
1576 if (strcmp(tn, event_name) == 0) {
1577 // match
1578 } else if (strcmp(en, event_name) == 0) {
1579 // match
1580 } else if (newStyleEventName) {
1581 // mismatch
1582 continue;
1583 } else { // for old style names, need more parsing
1584 bool match = false;
1585
1586 const char* s = en;
1587 for (int j=0; s[j]; j++) {
1588
1589 if ((event_name[j]==0) && (s[j]=='/')) {
1590 match = true;
1591 break;
1592 }
1593
1594 if ((event_name[j]==0) && (s[j]=='_')) {
1595 match = true;
1596 break;
1597 }
1598
1599 if (event_name[j]==0) {
1600 match = false;
1601 break;
1602 }
1603
1604 if (tolower(event_name[j]) != tolower(s[j])) {
1605 match = false;
1606 break;
1607 }
1608 }
1609
1610 if (!match)
1611 continue;
1612 }
1613
1614 if (fColumnNamesCache[tn].size() == 0)
1616
1617 for (unsigned j=0; j<fColumnsCache[tn].size(); j+=2) {
1618 const char* cn = fColumnsCache[tn][j].c_str();
1619 const char* name = fColumnNamesCache[tn][cn].c_str();
1620 if (strlen(name) < 1)
1621 name = cn;
1622
1623 char alt_tag_name[1024]; // maybe this is an array without "Names"?
1624 sprintf(alt_tag_name, "%s[%d]", tag_name, tag_index);
1625
1626 //printf(" looking for tag [%s] alt [%s], try column name [%s]\n", tag_name, alt_tag_name, name);
1627
1628 if (strcmp(cn, tag_name) != 0)
1629 if (strcmp(name, tag_name) != 0)
1630 if (strcmp(name, alt_tag_name) != 0)
1631 continue;
1632
1633 //printf("**found table [%s] column [%s]\n", tn, cn);
1634
1635 result->push_back(XItem(tn, cn));
1636 }
1637 }
1638
1639 return HS_SUCCESS;
1640 }
1641
1642
1643 /*------------------------------------------------------------------*/
1644
1645 int hs_get_last_written(time_t start_time, int num_var, const char* const event_name[], const char* const tag_name[], const int var_index[], time_t last_written[])
1646 {
1647 double dstart_time = start_time;
1648
1649 if (fDebug) {
1650 printf("hs_get_last_written: start time %.0f, num_var %d\n", dstart_time, num_var);
1651 }
1652
1653 for (int i=0; i<num_var; i++) {
1654 last_written[i] = 0;
1655
1657 FindItem(event_name[i], tag_name[i], var_index[i], &xitem);
1658
1659 if (fDebug) {
1660 printf("For event [%s] tag [%s] index [%d] found %d entries: ", event_name[i], tag_name[i], var_index[i], (int)xitem.size());
1661 for (unsigned j=0; j<xitem.size(); j++) {
1662 printf(" table [%s], column [%s]", xitem[j].tableName.c_str(), xitem[j].columnName.c_str());
1663 }
1664 printf("\n");
1665 }
1666
1667 if (xitem.size() < 1) // not found
1668 continue;
1669
1670 time_t tt = 0;
1671
1672 for (unsigned j=0; j<xitem.size(); j++) {
1673 const char* tn = xitem[j].tableName.c_str();
1674 const char* cn = xitem[j].columnName.c_str();
1675
1676 std::string cmd;
1677 cmd = "SELECT _i_time, \"";
1678 cmd += cn;
1679 cmd += "\" FROM \'";
1680 cmd += tn;
1681 cmd += "\' WHERE _i_time <= ";
1682 cmd += TimeToString(dstart_time);
1683 cmd += " ORDER BY _i_time DESC LIMIT 2;";
1684
1686
1687 int status = fSql->Prepare(tn, cmd.c_str(), &st);
1688
1689 if (fDebug) {
1690 printf("hs_get_last_written: event \"%s\", tag \"%s\", index %d: Read table \"%s\" column \"%s\": status %d\n",
1691 event_name[i], tag_name[i], var_index[i],
1692 tn,
1693 cn,
1694 status
1695 );
1696 }
1697
1698 if (status != DB_SUCCESS) {
1699 continue;
1700 }
1701
1702 /* Loop through the rows in the result-set */
1703
1704 for (int k=0; ; k++) {
1705 status = fSql->Step(st);
1706 if (status != DB_SUCCESS)
1707 break;
1708
1709 time_t t = fSql->GetInt64(st, 0);
1710 double v = fSql->GetDouble(st, 1);
1711
1712 if (t > start_time)
1713 continue;
1714
1715 if (0) {
1716 if (k<10)
1717 printf("count %d, t %d, v %f, tt %d\n", k, (int)t, v, (int)tt);
1718 }
1719
1720 if (t > tt)
1721 tt = t;
1722 }
1723
1724 fSql->Finalize(&st);
1725 }
1726
1727 last_written[i] = tt;
1728 }
1729
1730 if (fDebug) {
1731 printf("hs_get_last_written: start time %.0f, num_var %d\n", dstart_time, num_var);
1732 for (int i=0; i<num_var; i++) {
1733 printf(" event [%s] tag [%s] index [%d] last_written %d\n", event_name[i], tag_name[i], var_index[i], (int)last_written[i]);
1734 }
1735 }
1736
1737 return HS_SUCCESS;
1738 }
1739
1740 /*------------------------------------------------------------------*/
1741
1742 int hs_read_table(double start_time, double end_time,
1743 const std::string& tn,
1744 int num_var, const XItemVector vvv[],
1746 int xstatus[])
1747 {
1748 if (fDebug)
1749 printf("hs_read_table: table [%s], start %f, end %f, dt %f\n", tn.c_str(), start_time, end_time, end_time-start_time);
1750
1751#if 0
1752 if (1) {
1753 printf("For event [%s] tag [%s] index [%d] found %d entries: ", event_name, tag_name, tag_index, (int)xitem.size());
1754 for (unsigned j=0; j<xitem.size(); j++) {
1755 printf(" table [%s], column [%s]", xitem[j].tableName.c_str(), xitem[j].columnName.c_str());
1756 }
1757 printf("\n");
1758 }
1759
1760 if (xitem.size() < 1) // not found
1761 return HS_UNDEFINED_VAR;
1762#endif
1763
1766
1767 std::string collist;
1768
1769 for (int i=0; i<num_var; i++) {
1770 for (unsigned j=0; j<vvv[i].size(); j++) {
1771 if (vvv[i][j].tableName == tn) {
1772 colnames.push_back(vvv[i][j].columnName);
1773 colindex.push_back(i);
1774
1775 if (collist.length() > 0)
1776 collist += ", ";
1777 collist += std::string("\"") + vvv[i][j].columnName + "\"";
1778 }
1779 }
1780 }
1781
1782 int numcol = (int)colnames.size();
1783
1784 if (fDebug) {
1785 printf("From table [%s]\n", tn.c_str());
1786 for (int k=0; k<numcol; k++) {
1787 printf("read column [%s] var index [%d]\n", colnames[k].c_str(), colindex[k]);
1788 }
1789 }
1790
1791 std::string cmd;
1792 cmd += "SELECT _i_time, ";
1793 cmd += collist;
1794 cmd += " FROM \'";
1795 cmd += tn;
1796 cmd += "\' WHERE _i_time>=";
1797 cmd += TimeToString(start_time);
1798 cmd += " and _i_time<=";
1799 cmd += TimeToString(end_time);
1800 cmd += " ORDER BY _i_time;";
1801
1802 if (fDebug) {
1803 printf("hs_read_table: cmd %s\n", cmd.c_str());
1804 }
1805
1807
1808 int status = fSql->Prepare(tn.c_str(), cmd.c_str(), &st);
1809
1810 if (fDebug) {
1811 printf("hs_read_table: Read table \"%s\" columns \"%s\": status %d\n", tn.c_str(), collist.c_str(), status);
1812 }
1813
1814 if (status != DB_SUCCESS) {
1815 //for (unsigned k=0; k<colnames.size(); k++)
1816 //xstatus[colindex[k]] = HS_FILE_ERROR;
1817 return HS_FILE_ERROR;
1818 }
1819
1820 /* Loop through the rows in the result-set */
1821
1822 int count = 0;
1823
1824 while (1) {
1825 status = fSql->Step(st);
1826 if (status != DB_SUCCESS)
1827 break;
1828
1829 count++;
1830
1831 time_t t = fSql->GetInt64(st, 0);
1832
1833 if (t < start_time || t > end_time)
1834 continue;
1835
1836 for (int k=0; k<numcol; k++) {
1837 double v = fSql->GetDouble(st, 1+k);
1838
1839 //printf("Column %d, index %d, Row %d, time %d, value %f\n", k, colindex[k], count, t, v);
1840
1841 buffer[colindex[k]]->Add(t, v);
1842 }
1843 }
1844
1845 fSql->Finalize(&st);
1846
1847 for (unsigned k=0; k<colnames.size(); k++)
1849
1850 if (fDebug)
1851 printf("hs_read_table: read %d rows\n", count);
1852
1853 return HS_SUCCESS;
1854 }
1855
1856 /*------------------------------------------------------------------*/
1857
1858 int hs_read_buffer(time_t start_time, time_t end_time,
1859 int num_var, const char* const event_name[], const char* const tag_name[], const int tag_index[],
1861 int status[])
1862 {
1863 if (fDebug)
1864 printf("hs_read_buffer: %d variables\n", num_var);
1865
1866 if (!fSql->IsConnected())
1867 return HS_FILE_ERROR;
1868
1869 for (int i=0; i<num_var; i++) {
1871 }
1872
1875
1876 for (int i=0; i<num_var; i++) {
1877
1878 if (event_name[i]==NULL) {
1880 continue;
1881 }
1882
1883 FindItem(event_name[i], tag_name[i], tag_index[i], &vvv[i]);
1884
1885 for (unsigned j=0; j<vvv[i].size(); j++) {
1886 bool found = false;
1887 for (unsigned k=0; k<ttt.size(); k++)
1888 if (ttt[k] == vvv[i][j].tableName) {
1889 found = true;
1890 break;
1891 }
1892
1893 if (!found)
1894 ttt.push_back(vvv[i][j].tableName);
1895 }
1896 }
1897
1898 if (fDebug) {
1899 for (int i=0; i<num_var; i++) {
1900 printf("For event [%s] tag [%s] index [%d] found %d entries: ", event_name[i], tag_name[i], tag_index[i], (int)vvv[i].size());
1901 for (unsigned j=0; j<vvv[i].size(); j++) {
1902 printf(" table [%s], column [%s]", vvv[i][j].tableName.c_str(), vvv[i][j].columnName.c_str());
1903 }
1904 printf("\n");
1905 }
1906 printf("Tables:");
1907 for (unsigned k=0; k<ttt.size(); k++)
1908 printf(" %s", ttt[k].c_str());
1909 printf("\n");
1910 }
1911
1912 for (unsigned k=0; k<ttt.size(); k++) {
1913 const std::string tn = ttt[k].c_str();
1914
1915 hs_read_table(start_time, end_time,
1916 tn,
1917 num_var, vvv,
1918 buffer,
1919 status);
1920 }
1921
1922 return HS_SUCCESS;
1923 }
1924
1925 /*------------------------------------------------------------------*/
1926
1927 class ReadBuffer: public MidasHistoryBufferInterface
1928 {
1929 public:
1933
1934 int fNumAdded;
1935
1936 int fNumAlloc;
1937 int *fNumEntries;
1939 double **fDataBuffer;
1940
1942
1943 ReadBuffer(time_t first_time, time_t last_time, time_t interval) // ctor
1944 {
1945 fNumAdded = 0;
1946
1950
1951 fNumAlloc = 0;
1952 fNumEntries = NULL;
1953 fTimeBuffer = NULL;
1954 fDataBuffer = NULL;
1955
1956 fPrevTime = 0;
1957 }
1958
1959 ~ReadBuffer() // dtor
1960 {
1961 }
1962
1963 void Realloc(int wantalloc)
1964 {
1965 if (wantalloc < fNumAlloc - 10)
1966 return;
1967
1968 int newalloc = fNumAlloc*2;
1969
1970 if (newalloc <= 1000)
1971 newalloc = wantalloc + 1000;
1972
1973 //printf("wantalloc %d, fNumEntries %d, fNumAlloc %d, newalloc %d\n", wantalloc, *fNumEntries, fNumAlloc, newalloc);
1974
1976 assert(*fTimeBuffer);
1977
1978 *fDataBuffer = (double*)realloc(*fDataBuffer, sizeof(double)*newalloc);
1979 assert(*fDataBuffer);
1980
1982 }
1983
1984 void Add(time_t t, double v)
1985 {
1986 if (t < fFirstTime)
1987 return;
1988 if (t > fLastTime)
1989 return;
1990
1991 fNumAdded++;
1992
1993 if ((fPrevTime==0) || (t >= fPrevTime + fInterval)) {
1994 int pos = *fNumEntries;
1995
1996 Realloc(pos + 1);
1997
1998 (*fTimeBuffer)[pos] = t;
1999 (*fDataBuffer)[pos] = v;
2000
2001 (*fNumEntries) = pos + 1;
2002
2003 fPrevTime = t;
2004 }
2005 }
2006
2007 void Finish()
2008 {
2009
2010 }
2011 };
2012
2013 /*------------------------------------------------------------------*/
2014
2015 int hs_read(time_t start_time, time_t end_time, time_t interval,
2016 int num_var,
2017 const char* const event_name[], const char* const tag_name[], const int var_index[],
2018 int num_entries[],
2019 time_t* time_buffer[], double* data_buffer[],
2020 int st[])
2021 {
2022 int status;
2023
2024 ReadBuffer** buffer = new ReadBuffer*[num_var];
2026
2027 for (int i=0; i<num_var; i++) {
2028 buffer[i] = new ReadBuffer(start_time, end_time, interval);
2029 bi[i] = buffer[i];
2030
2031 // make sure outputs are initialized to something sane
2032 if (num_entries)
2033 num_entries[i] = 0;
2034 if (time_buffer)
2035 time_buffer[i] = NULL;
2036 if (data_buffer)
2037 data_buffer[i] = NULL;
2038 if (st)
2039 st[i] = 0;
2040
2041 if (num_entries)
2042 buffer[i]->fNumEntries = &num_entries[i];
2043 if (time_buffer)
2044 buffer[i]->fTimeBuffer = &time_buffer[i];
2045 if (data_buffer)
2046 buffer[i]->fDataBuffer = &data_buffer[i];
2047 }
2048
2049 status = hs_read_buffer(start_time, end_time,
2050 num_var, event_name, tag_name, var_index,
2051 bi, st);
2052
2053 for (int i=0; i<num_var; i++) {
2054 buffer[i]->Finish();
2055 delete buffer[i];
2056 }
2057
2058 delete buffer;
2059 delete bi;
2060
2061 return status;
2062 }
2063
2064 /*------------------------------------------------------------------*/
2065
2067 {
2068 public:
2069 int fNumBins;
2070 time_t fFirstTime;
2071 time_t fLastTime;
2072
2073 int fNumEntries;
2074 double *fSum0;
2075 double *fSum1;
2076 double *fSum2;
2077
2078 int *fCount;
2079 double *fMean;
2080 double *fRms;
2081 double *fMin;
2082 double *fMax;
2083
2084 time_t *fLastTimePtr;
2085 double *fLastValuePtr;
2086
2088 {
2089 fNumEntries = 0;
2090
2091 fNumBins = num_bins;
2092 fFirstTime = first_time;
2093 fLastTime = last_time;
2094
2095 fSum0 = new double[num_bins];
2096 fSum1 = new double[num_bins];
2097 fSum2 = new double[num_bins];
2098
2099 for (int i=0; i<num_bins; i++) {
2100 fSum0[i] = 0;
2101 fSum1[i] = 0;
2102 fSum2[i] = 0;
2103 }
2104
2105 fMean = NULL;
2106 fRms = NULL;
2107 fMin = NULL;
2108 fMax = NULL;
2109 fLastTimePtr = NULL;
2110 fLastValuePtr = NULL;
2111 }
2112
2113 ~BinnedBuffer() // dtor
2114 {
2115 delete fSum0;
2116 delete fSum1;
2117 delete fSum2;
2118 }
2119
2120 void Add(time_t t, double v)
2121 {
2122 if (t < fFirstTime)
2123 return;
2124 if (t > fLastTime)
2125 return;
2126
2127 fNumEntries++;
2128
2129 double a = t - fFirstTime;
2130 double b = fLastTime - fFirstTime;
2131 double fbin = fNumBins*a/b;
2132
2133 int ibin = fbin;
2134
2135 if (ibin < 0)
2136 ibin = 0;
2137 else if (ibin >= fNumBins)
2138 ibin = fNumBins;
2139
2140 if (fSum0[ibin] == 0) {
2141 if (fMin)
2142 fMin[ibin] = v;
2143 if (fMax)
2144 fMax[ibin] = v;
2145 if (fLastTimePtr)
2146 *fLastTimePtr = t;
2147 if (fLastValuePtr)
2148 *fLastValuePtr = v;
2149 }
2150
2151 fSum0[ibin] += 1.0;
2152 fSum1[ibin] += v;
2153 fSum2[ibin] += v*v;
2154
2155 if (fMin)
2156 if (v < fMin[ibin])
2157 fMin[ibin] = v;
2158
2159 if (fMax)
2160 if (v > fMax[ibin])
2161 fMax[ibin] = v;
2162
2163 if (fLastTimePtr)
2164 if (t > *fLastTimePtr) {
2165 *fLastTimePtr = t;
2166 if (fLastValuePtr)
2167 *fLastValuePtr = v;
2168 }
2169 }
2170
2171 void Finish()
2172 {
2173 for (int i=0; i<fNumBins; i++) {
2174 double num = fSum0[i];
2175 double mean = fSum1[i]/num;
2176 double variance = fSum2[i]/num-mean*mean;
2177 double rms = 0;
2178 if (variance > 0)
2179 rms = sqrt(variance);
2180
2181 if (fCount)
2182 fCount[i] = num;
2183
2184 if (fMean)
2185 fMean[i] = mean;
2186
2187 if (fRms)
2188 fRms[i] = rms;
2189 }
2190 }
2191 };
2192
2193 int hs_read_binned(time_t start_time, time_t end_time, int num_bins,
2194 int num_var, const char* const event_name[], const char* const tag_name[], const int var_index[],
2195 int num_entries[],
2196 int* count_bins[], double* mean_bins[], double* rms_bins[], double* min_bins[], double* max_bins[],
2197 time_t last_time[], double last_value[],
2198 int st[])
2199 {
2200 int status;
2201
2202 BinnedBuffer** buffer = new BinnedBuffer*[num_var];
2204
2205 for (int i=0; i<num_var; i++) {
2206 buffer[i] = new BinnedBuffer(start_time, end_time, num_bins);
2207 xbuffer[i] = buffer[i];
2208
2209 if (count_bins)
2210 buffer[i]->fCount = count_bins[i];
2211 if (mean_bins)
2212 buffer[i]->fMean = mean_bins[i];
2213 if (rms_bins)
2214 buffer[i]->fRms = rms_bins[i];
2215 if (min_bins)
2216 buffer[i]->fMin = min_bins[i];
2217 if (max_bins)
2218 buffer[i]->fMax = max_bins[i];
2219 if (last_time)
2220 buffer[i]->fLastTimePtr = &last_time[i];
2221 if (last_value)
2222 buffer[i]->fLastValuePtr = &last_value[i];
2223 }
2224
2225 status = hs_read_buffer(start_time, end_time,
2226 num_var, event_name, tag_name, var_index,
2227 xbuffer,
2228 st);
2229
2230 for (int i=0; i<num_var; i++) {
2231 buffer[i]->Finish();
2232 delete buffer[i];
2233 }
2234
2235 delete buffer;
2236 delete xbuffer;
2237
2238 return status;
2239 }
2240};
2241
2243// Factory constructors //
2245
2247{
2248 return new SqliteHistory();
2249}
2250
2251#else // HAVE_SQLITE
2252
2257
2258#endif
2259
2260/* emacs
2261 * Local Variables:
2262 * tab-width: 8
2263 * c-basic-offset: 3
2264 * indent-tabs-mode: nil
2265 * End:
2266 */
virtual void Add(time_t time, double value)=0
char type[NAME_LENGTH]
history channel name
Definition history.h:112
char name[NAME_LENGTH]
Definition history.h:111
void Add(time_t t, double v)
std::vector< HsSchema * > fEvents
int hs_get_tags(const char *event_name, time_t t, std::vector< TAG > *ptags)
get list of history variables for given event (use event names returned by hs_get_events()) that exis...
int hs_write_event(const char *event_name, time_t timestamp, int buffer_size, const char *buffer)
see hs_write_event(), returns HS_SUCCESS or HS_FILE_ERROR
int hs_read_buffer(time_t start_time, time_t end_time, int num_var, const char *const event_name[], const char *const var_name[], const int var_index[], MidasHistoryBufferInterface *buffer[], int hs_status[])
returns HS_SUCCESS
int hs_define_event(const char *event_name, time_t timestamp, int ntags, const TAG tags[])
see hs_define_event(), returns HS_SUCCESS or HS_FILE_ERROR
int hs_read_binned(time_t start_time, time_t end_time, int num_bins, int num_var, const char *const event_name[], const char *const var_name[], const int var_index[], int num_entries[], int *count_bins[], double *mean_bins[], double *rms_bins[], double *min_bins[], double *max_bins[], time_t *bins_first_time[], double *bins_first_value[], time_t *bins_last_time[], double *bins_last_value[], time_t last_time[], double last_value[], int st[])
returns HS_SUCCESS
int hs_get_events(time_t t, std::vector< std::string > *pevents)
get list of events that exist(ed) at given time and later (value 0 means "return all events from begi...
int hs_get_last_written(time_t timestamp, int num_var, const char *const event_name[], const char *const var_name[], const int var_index[], time_t last_written[])
int hs_read(time_t start_time, time_t end_time, time_t interval, int num_var, const char *const event_name[], const char *const var_name[], const int var_index[], int num_entries[], time_t *time_buffer[], double *data_buffer[], int st[])
see hs_read(), returns HS_SUCCESS
int hs_clear_cache()
clear internal cache, returns HS_SUCCESS
int hs_flush_buffers()
flush buffered data to storage where it is visible to mhttpd
virtual int Finalize()=0
virtual int ListColumns(const char *table, std::vector< std::string > *plist)=0
virtual double GetDouble(int column)=0
virtual bool IsConnected()=0
virtual int ListTables(std::vector< std::string > *plist)=0
virtual int Disconnect()=0
virtual int Prepare(const char *table_name, const char *sql)=0
virtual int Exec(const char *sql)=0
virtual int Connect(const char *dsn=0)=0
virtual int Step()=0
virtual const char * GetText(int column)=0
virtual int OpenTransaction(const char *table_name)=0
int hs_disconnect()
disconnect from history, returns HS_SUCCESS
int hs_set_debug(int debug)
set debug level, returns previous debug level
int hs_connect(const char *connect_string)
returns HS_SUCCESS
#define DB_FILE_ERROR
Definition midas.h:647
#define DB_SUCCESS
Definition midas.h:631
#define DB_NO_MORE_SUBKEYS
Definition midas.h:646
#define HS_UNDEFINED_VAR
Definition midas.h:733
#define HS_SUCCESS
Definition midas.h:727
#define HS_FILE_ERROR
Definition midas.h:728
#define HS_UNDEFINED_EVENT
Definition midas.h:732
#define TID_WORD
Definition midas.h:332
#define MINFO
Definition midas.h:560
#define TID_BYTE
Definition midas.h:327
#define MERROR
Definition midas.h:559
#define TID_FLOAT
Definition midas.h:341
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:915
static std::string MidasNameToSqlName(const char *s)
int WriteEvent(SqlBase *sql, Event *e, time_t t, const char *buf, int size)
static const int tid_size[]
static int sql2midasType(const char *name)
static const char * midasTypeName(int tid)
static bool isCompatible(int tid, const char *sqlType)
static const char * tid_name[]
static void PrintTags(int ntags, const TAG tags[])
static const char * midas2sqlType(int tid)
static const char ** sql_type
static std::string MidasNameToSqlName(const char *s)
static std::string TimeToString(time_t t)
static void PrintTags(int ntags, const TAG tags[])
static std::string SmallIntToString(int i)
std::map< std::string, StringMap > StringMapMap
MidasHistoryInterface * MakeMidasHistorySqlite()
std::vector< int > IntVector
std::vector< std::string > StringVector
std::map< std::string, std::string > StringMap
std::map< std::string, StringVector > StringVectorMap
DWORD n[4]
Definition mana.cxx:247
DWORD last_time
Definition mana.cxx:3070
BOOL debug
debug printouts
Definition mana.cxx:254
INT type
Definition mana.cxx:269
double count
Definition mdump.cxx:33
INT i
Definition mdump.cxx:32
static int offset
Definition mgd.cxx:1500
#define name(x)
Definition midas_macro.h:24
struct callback_addr callback
Definition mserver.cxx:22
MUTEX_T * tm
Definition odbedit.cxx:39
BOOL match(char *pat, char *str)
Definition odbedit.cxx:190
INT j
Definition odbhist.cxx:40
INT k
Definition odbhist.cxx:40
DWORD status
Definition odbhist.cxx:39
TH1X EXPRT * h1_book(const char *name, const char *title, int bins, double min, double max)
Definition rmidas.h:24
std::string table_name
bool active
std::string event_name
std::vector< Tag > tags
Definition midas.h:1234
DWORD type
Definition midas.h:1236
DWORD n_data
Definition midas.h:1237
char name[NAME_LENGTH]
Definition midas.h:1235
int offset
bool create
std::string column_name
char c
Definition system.cxx:1310
@ DIR
Definition test_init.cxx:7
static double e(void)
Definition tinyexpr.c:136