MIDAS
Loading...
Searching...
No Matches
history_odbc.cxx
Go to the documentation of this file.
1/********************************************************************\
2
3 Name: history_odbc.cxx
4 Created by: Konstantin Olchanski
5
6 Contents: Interface class for writing MIDAS history data to SQL databases throught the ODBC interface layer
7
8\********************************************************************/
9
10#include "midas.h"
11#include "msystem.h"
12
13#include <vector>
14#include <string>
15
17// MIDAS includes //
19
20#include "midas.h"
21#include "history.h"
22#include "mstrlcpy.h"
23
25// helper stuff //
27
28#define FREE(x) { if (x) free(x); (x) = NULL; }
29
31// Definitions extracted from midas.c //
33
34/********************************************************************/
35/* data type sizes */
36static const int tid_size[] = {
37 0, /* tid == 0 not defined */
38 1, /* TID_UINT8 unsigned byte 0 255 */
39 1, /* TID_INT8 signed byte -128 127 */
40 1, /* TID_CHAR single character 0 255 */
41 2, /* TID_UINT16 two bytes 0 65535 */
42 2, /* TID_INT16 signed word -32768 32767 */
43 4, /* TID_UINT32 four bytes 0 2^32-1 */
44 4, /* TID_INT32 signed dword -2^31 2^31-1 */
45 4, /* TID_BOOL four bytes bool 0 1 */
46 4, /* TID_FLOAT 4 Byte float format */
47 8, /* TID_DOUBLE 8 Byte float format */
48 4, /* TID_BITFIELD 32 Bits Bitfield 00000... 11111... */
49 0, /* TID_STRING zero terminated string */
50 0, /* TID_ARRAY variable length array of unkown type */
51 0, /* TID_STRUCT C structure */
52 0, /* TID_KEY key in online database */
53 0, /* TID_LINK link in online database */
54 8, /* TID_INT64 8 bytes int -2^63 2^63-1 */
55 8 /* TID_UINT64 8 bytes unsigned int 0 2^64-1 */
56};
57
58/* data type names */
59static const char *tid_name[] = {
60 "NULL",
61 "UINT8",
62 "INT8",
63 "CHAR",
64 "UINT16",
65 "INT16",
66 "UINT32",
67 "INT32",
68 "BOOL",
69 "FLOAT",
70 "DOUBLE",
71 "BITFIELD",
72 "STRING",
73 "ARRAY",
74 "STRUCT",
75 "KEY",
76 "LINK",
77 "INT64",
78 "UINT64"
79};
80
81// SQL types
82#ifdef HAVE_ODBC
83static const char *sql_type_pgsql[] = {
84 "xxxINVALIDxxxNULL", // TID_NULL
85 "SMALLINT", // MYSQL "TINYINT SIGNED", // TID_BYTE
86 "SMALLINT", // MYSQL "TINYINT UNSIGNED", // TID_SBYTE
87 "CHAR(1)", // TID_CHAR
88 "SMALLINT", // MYSQL "SMALLINT UNSIGNED ", // TID_WORD
89 "SMALLINT", // MYSQL "SMALLINT SIGNED ", // TID_SHORT
90 "INTEGER", // MYSQL "INT UNSIGNED ", // TID_DWORD
91 "INTEGER", // MYSQL "INT SIGNED ", // TID_INT
92 "BOOL", // TID_BOOL
93 "FLOAT(53)", // MYSQL "DOUBLE" TID_FLOAT
94 "FLOAT(53)", // MYSQL "DOUBLE" TID_DOUBLE
95 "INTEGER", // MYSQL "INT UNSIGNED", // TID_BITFIELD
96 "VARCHAR", // TID_STRING
97 "xxxINVALIDxxxARRAY",
98 "xxxINVALIDxxxSTRUCT",
99 "xxxINVALIDxxxKEY",
100 "xxxINVALIDxxxLINK"
101};
102#endif
103
104static const char *sql_type_mysql[] = {
105 "xxxINVALIDxxxNULL", // TID_NULL
106 "tinyint unsigned", // TID_UINT8
107 "tinyint", // TID_INT8
108 "char", // TID_CHAR
109 "smallint unsigned", // TID_UINT16
110 "smallint", // TID_INT16
111 "integer unsigned", // TID_UINT32
112 "integer", // TID_INT32
113 "tinyint", // TID_BOOL
114 "float", // TID_FLOAT
115 "double", // TID_DOUBLE
116 "integer unsigned", // TID_BITFIELD
117 "VARCHAR", // TID_STRING
118 "xxxINVALIDxxxARRAY",
119 "xxxINVALIDxxxSTRUCT",
120 "xxxINVALIDxxxKEY",
121 "xxxINVALIDxxxLINK"
122};
123
125// Handling of data types //
127
128static const char **sql_type = NULL;
129
130static const char* midasTypeName(int tid)
131{
132 assert(tid>=0);
133 assert(tid<15);
134 return tid_name[tid];
135}
136
137static const char* midas2sqlType(int tid)
138{
139 assert(tid>=0);
140 assert(tid<15);
141 return sql_type[tid];
142}
143
144static int sql2midasType(const char* name)
145{
146 for (int tid=0; tid<15; tid++)
147 if (strcasecmp(name, sql_type[tid])==0)
148 return tid;
149 printf("sql2midasType: Cannot convert SQL data type \'%s\' to a MIDAS data type!\n", name);
150 return 0;
151}
152
153static bool isCompatible(int tid, const char* sqlType)
154{
155#if 0
156 printf("compare types midas \'%s\'=\'%s\' and sql \'%s\'\n", midasTypeName(tid), midas2sqlType(tid), sqlType);
157#endif
158
159 if (sql2midasType(sqlType) == tid)
160 return true;
161
162 if (strcasecmp(midas2sqlType(tid), sqlType) == 0)
163 return true;
164
165 // permit writing FLOAT into DOUBLE
166 if (tid==TID_FLOAT && strcmp(sqlType, "double")==0)
167 return true;
168
169 // T2K quirk!
170 // permit writing BYTE into signed tinyint
171 if (tid==TID_UINT8 && strcmp(sqlType, "tinyint")==0)
172 return true;
173
174 // T2K quirk!
175 // permit writing WORD into signed tinyint
176 if (tid==TID_UINT16 && strcmp(sqlType, "tinyint")==0)
177 return true;
178
179 return false;
180}
181
183// Base class for access to SQL functions //
185
187{
188public:
189 virtual int SetDebug(int debug) = 0;
190 virtual int Connect(const char* dsn = 0) = 0;
191 virtual int Disconnect() = 0;
192 virtual bool IsConnected() = 0;
193 virtual int Exec(const char* sql) = 0;
194 virtual int GetNumRows() = 0;
195 virtual int GetNumColumns() = 0;
196 virtual int Fetch() = 0;
197 virtual int Done() = 0;
198 virtual int ListTables(std::vector<std::string> *plist) = 0;
199 virtual int ListColumns(const char* table, std::vector<std::string> *plist) = 0;
200 virtual const char* GetColumn(int icol) = 0;
201 virtual ~SqlBase() { }; // virtual dtor
202};
203
205// SqlDebug: for debugging: write all SQL commands to stdout //
207
208class SqlDebug: public SqlBase
209{
210public:
214
215public:
216
217 SqlDebug() // ctor
218 {
219 fp = NULL;
220 fIsConnected = false;
221 }
222
223 ~SqlDebug() // dtor
224 {
225 if (fp)
226 fclose(fp);
227 fp = NULL;
228 }
229
231 {
232 int old_debug = fDebug;
233 fDebug = debug;
234 return old_debug;
235 }
236
237 int Connect(const char* filename = NULL)
238 {
239 if (!filename)
240 filename = "/dev/fd/1";
241 fp = fopen(filename, "w");
242 assert(fp);
244 fIsConnected = true;
245 return DB_SUCCESS;
246 }
247
248 int Exec(const char* sql)
249 {
250 fprintf(fp, "%s\n", sql);
251 return DB_SUCCESS;
252 }
253
255 {
256 // do nothing
257 fIsConnected = false;
258 return DB_SUCCESS;
259 }
260
262 {
263 return fIsConnected;
264 }
265
266 int GetNumRows() { return DB_SUCCESS; }
267 int GetNumColumns() { return DB_SUCCESS; }
268 int Fetch() { return DB_NO_MORE_SUBKEYS; }
269 int Done() { return DB_SUCCESS; }
270 int ListTables(std::vector<std::string> *plist) { return DB_SUCCESS; };
271 int ListColumns(const char* table, std::vector<std::string> *plist) { return DB_SUCCESS; };
272 const char* GetColumn(int icol) { return NULL; };
273};
274
275#ifdef HAVE_ODBC
276
278// ODBC includes //
280
281// MIDAS defines collide with ODBC
282
283#define DWORD DWORD_xxx
284#define BOOL BOOL_xxx
285
286#include <sql.h>
287#include <sqlext.h>
288#include <sqltypes.h>
289
291// SqlODBC: SQL access through ODBC //
293
294class SqlODBC: public SqlBase
295{
296public:
297 bool fIsConnected;
298
299 std::string fDSN;
300
301 int fDebug;
302
304 SQLHDBC fDB;
306
307 SqlODBC(); // ctor
308 ~SqlODBC(); // dtor
309
310 int SetDebug(int debug)
311 {
312 int old_debug = fDebug;
313 fDebug = debug;
314 return old_debug;
315 }
316
317 int Connect(const char* dsn);
318 int Disconnect();
319 bool IsConnected();
320
321 int ListTables(std::vector<std::string> *plist);
322 int ListColumns(const char* table_name, std::vector<std::string> *plist);
323
324 int Exec(const char* sql);
325
326 int GetNumRows();
327 int GetNumColumns();
328 int Fetch();
329 const char* GetColumn(int icol);
330 int Done();
331
332protected:
333 void ReportErrors(const char* from, const char* sqlfunc, int status);
334 int DecodeError();
335};
336
337SqlODBC::SqlODBC() // ctor
338{
339 fIsConnected = false;
340 fDebug = 0;
341}
342
343SqlODBC::~SqlODBC() // dtor
344{
345 Disconnect();
346}
347
348int SqlODBC::Connect(const char* dsn)
349{
350 if (fIsConnected)
351 Disconnect();
352
353 fDSN = dsn;
354
356
357 if (!SQL_SUCCEEDED(status)) {
358 cm_msg(MERROR, "SqlODBC::Connect", "SQLAllocHandle(SQL_HANDLE_ENV) error %d", status);
359 return DB_FILE_ERROR;
360 }
361
364 (void*)SQL_OV_ODBC2,
365 0);
366 if (!SQL_SUCCEEDED(status)) {
367 cm_msg(MERROR, "SqlODBC::Connect", "SQLSetEnvAttr() error %d", status);
369 return DB_FILE_ERROR;
370 }
371
373 if (!SQL_SUCCEEDED(status)) {
374 cm_msg(MERROR, "SqlODBC::Connect", "SQLAllocHandle(SQL_HANDLE_DBC) error %d", status);
376 return DB_FILE_ERROR;
377 }
378
380
381 if (0) {
382 // connect to PgSQL database
383
386 (SQLCHAR*) "xxx", SQL_NTS,
387 (SQLCHAR*) "", SQL_NTS);
388 }
389
390 if (1) {
391 // connect to MySQL database
392
395 (SQLCHAR*) NULL, SQL_NTS,
396 (SQLCHAR*) NULL, SQL_NTS);
397 }
398
402 SQLCHAR V_OD_stat[10]; // Status SQL
403 SQLCHAR V_OD_msg[200];
404
406 cm_msg(MERROR, "SqlODBC::Connect", "SQLConnect() error %d, %s (%d)", status, V_OD_msg,V_OD_err);
408 return DB_FILE_ERROR;
409 }
410
412
413 if (fDebug)
414 cm_msg(MINFO, "SqlODBC::Connect", "Connected to ODBC database DSN \'%s\'", dsn);
415
416 fIsConnected = true;
417
418 return DB_SUCCESS;
419}
420
421int SqlODBC::Disconnect()
422{
423 if (!fIsConnected)
424 return DB_SUCCESS;
425
426 SQLDisconnect(fDB);
427
431
432 fIsConnected = false;
433
434 return DB_SUCCESS;
435}
436
437bool SqlODBC::IsConnected()
438{
439 return fIsConnected;
440}
441
442void SqlODBC::ReportErrors(const char* from, const char* sqlfunc, int status)
443{
444 if (fDebug)
445 printf("%s: %s error %d\n", from, sqlfunc, status);
446
447 for (int i=1; ; i++) {
448 SQLCHAR state[10]; // Status SQL
449 SQLINTEGER error;
450 SQLCHAR message[1024];
452
454 fStmt,
455 i,
456 state,
457 &error,
458 message,
459 sizeof(message),
460 &mlen);
461
462 if (status == SQL_NO_DATA)
463 break;
464
465 if (!SQL_SUCCEEDED(status)) {
466 cm_msg(MERROR, "SqlODBC::ReportErrors", "SQLGetDiagRec() error %d", status);
467 break;
468 }
469
470 // Catch error "MySQL has gone away" and turn it into a warning. The program should be trying to reconnect immediately
471 // and will produce an error if it fails
472 if (1 || (error == 2006) ) {
473 if (fDebug)
474 printf("%s: %s warning: state: \'%s\', message: \'%s\', native error: %d\n", from, sqlfunc, state, message, (int)error);
475 cm_msg(MINFO, from, "%s warning: state: \'%s\', message: \'%s\', native error: %d", sqlfunc, state, message, (int)error);
476 } else if (1 || ((error != 1060) && (error != 1050))) {
477 if (fDebug)
478 printf("%s: %s error: state: \'%s\', message: \'%s\', native error: %d\n", from, sqlfunc, state, message, (int)error);
479 cm_msg(MERROR, from, "%s error: state: \'%s\', message: \'%s\', native error: %d", sqlfunc, state, message, (int)error);
480 }
481 }
482}
483
484int SqlODBC::DecodeError()
485{
486 // returns:
487 // DB_SUCCESS
488 // DB_NO_KEY
489 // DB_KEY_EXIST
490
491 for (int i=1; ; i++) {
492 SQLCHAR state[10]; // Status SQL
493 SQLINTEGER error;
494 SQLCHAR message[1024];
496
497 error = 0;
498
500 fStmt,
501 i,
502 state,
503 &error,
504 message,
505 sizeof(message),
506 &mlen);
507
508 if (status == SQL_NO_DATA)
509 return DB_SUCCESS;
510
511 if (error==1146)
512 return DB_NO_KEY;
513
514 if (error==1050)
515 return DB_KEY_EXIST;
516 }
517}
518
519int SqlODBC::ListTables(std::vector<std::string> *plist)
520{
521 if (!fIsConnected)
522 return DB_FILE_ERROR;
523
524 for (int i=0; i<2; i++) {
525 if (fDebug)
526 printf("SqlODBC::ListTables!\n");
527
528 /* Retrieve a list of tables */
529 int status = SQLTables(fStmt, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR*)"TABLE", SQL_NTS);
530
532 break;
533
534 if (fDebug)
535 printf("SqlODBC::ListTables: SQLTables() error %d\n", status);
536
537 ReportErrors("SqlODBC::ListTables", "SQLTables()", status);
538
540
541 //if (status == DB_NO_KEY)
542 // return status;
543 //
544 //if (status == DB_KEY_EXIST)
545 // return status;
546
547 cm_msg(MINFO, "SqlODBC::ListTables", "Reconnecting to ODBC database DSN \'%s\'", fDSN.c_str());
548
549 // try to reconnect
550 std::string dsn = fDSN;
551 Disconnect();
552 status = Connect(dsn.c_str());
553
554 if (!fIsConnected) {
555 cm_msg(MERROR, "SqlODBC::ListTables", "Cannot reconnect to ODBC database DSN \'%s\', status %d. Database is down?", fDSN.c_str(), status);
556 return DB_FILE_ERROR;
557 }
558
559 cm_msg(MINFO, "SqlODBC::ListTables", "Reconnected to ODBC database DSN \'%s\'", fDSN.c_str());
560 }
561
562 int ncols = GetNumColumns();
563 int nrows = GetNumRows();
564
565 if (ncols <= 0 || nrows <= 0) {
566 cm_msg(MERROR, "SqlODBC::ListTables", "Error: SQLTables() returned unexpected number of columns %d or number of rows %d", ncols, nrows);
567 }
568
569 int row = 0;
570 while (1) {
571 int status = Fetch();
572 if (status != DB_SUCCESS)
573 break;
574
575 if (0) {
576 printf("row %d: ", row);
577 for (int i=1; i<=ncols; i++) {
578 const char* s = GetColumn(i);
579 printf("[%s]", s);
580 }
581 printf("\n");
582 row++;
583 }
584
585 plist->push_back(GetColumn(3));
586 }
587
588 Done();
589
590 return DB_SUCCESS;
591}
592
593int SqlODBC::ListColumns(const char* table, std::vector<std::string> *plist)
594{
595 if (!fIsConnected)
596 return DB_FILE_ERROR;
597
598 for (int i=0; i<2; i++) {
599 if (fDebug)
600 printf("SqlODBC::ListColumns for table \'%s\'\n", table);
601
602 /* Retrieve a list of columns */
603 int status = SQLColumns(fStmt, NULL, 0, NULL, 0, (SQLCHAR*)table, SQL_NTS, NULL, 0);
604
606 break;
607
608 if (fDebug)
609 printf("SqlODBC::ListColumns: SQLColumns(%s) error %d\n", table, status);
610
611 ReportErrors("SqlODBC::ListColumns", "SQLColumns()", status);
612
614
615 //if (status == DB_NO_KEY)
616 // return status;
617 //
618 //if (status == DB_KEY_EXIST)
619 // return status;
620
621 cm_msg(MINFO, "SqlODBC::ListColumns", "Reconnecting to ODBC database DSN \'%s\'", fDSN.c_str());
622
623 // try to reconnect
624 std::string dsn = fDSN;
625 Disconnect();
626 status = Connect(dsn.c_str());
627
628 if (!fIsConnected) {
629 cm_msg(MERROR, "SqlODBC::ListColumns", "Cannot reconnect to ODBC database DSN \'%s\', status %d. Database is down?", fDSN.c_str(), status);
630 return DB_FILE_ERROR;
631 }
632
633 cm_msg(MINFO, "SqlODBC::ListColumns", "Reconnected to ODBC database DSN \'%s\'", fDSN.c_str());
634 }
635
636 int ncols = GetNumColumns();
637 int nrows = GetNumRows(); // nrows seems to be always "-1"
638
639 if (ncols <= 0 /*|| nrows <= 0*/) {
640 cm_msg(MERROR, "SqlODBC::ListColumns", "Error: SQLColumns(\'%s\') returned unexpected number of columns %d or number of rows %d", table, ncols, nrows);
641 }
642
643 //printf("get columns [%s]: status %d, ncols %d, nrows %d\n", table, status, ncols, nrows);
644
645 int row = 0;
646 while (1) {
647 int status = Fetch();
648 if (status != DB_SUCCESS)
649 break;
650
651 if (0) {
652 printf("row %d: ", row);
653 for (int i=1; i<=ncols; i++) {
654 const char* s = GetColumn(i);
655 printf("[%s]", s);
656 }
657 printf("\n");
658 row++;
659 }
660
661 plist->push_back(GetColumn(4)); // column name
662 plist->push_back(GetColumn(6)); // column type
663 }
664
665 Done();
666
667 return DB_SUCCESS;
668}
669
670int SqlODBC::Exec(const char* sql)
671{
672 // return values:
673 // DB_SUCCESS
674 // DB_FILE_ERROR: not connected
675 // DB_NO_KEY: "table not found"
676
677 if (!fIsConnected)
678 return DB_FILE_ERROR;
679
680 int status;
681
682 for (int i=0; i<2; i++) {
683 if (fDebug)
684 printf("SqlODBC::Exec: %s\n", sql);
685
687
688 if (SQL_SUCCEEDED(status)) {
689 return DB_SUCCESS;
690 }
691
692 if (fDebug)
693 printf("SqlODBC::Exec: SQLExecDirect() error %d: SQL command: \"%s\"\n", status, sql);
694
695 ReportErrors("SqlODBC::Exec", "SQLExecDirect()", status);
696
698
699 if (status == DB_NO_KEY)
700 return status;
701
702 if (status == DB_KEY_EXIST)
703 return status;
704
705 cm_msg(MINFO, "SqlODBC::Exec", "Reconnecting to ODBC database DSN \'%s\'", fDSN.c_str());
706
707 // try to reconnect
708 std::string dsn = fDSN;
709 Disconnect();
710 status = Connect(dsn.c_str());
711
712 if (!fIsConnected) {
713 cm_msg(MERROR, "SqlODBC::Exec", "Cannot reconnect to ODBC database DSN \'%s\', status %d. Database is down?", fDSN.c_str(), status);
714 return DB_FILE_ERROR;
715 }
716
717 cm_msg(MINFO, "SqlODBC::Exec", "Reconnected to ODBC database DSN \'%s\'", fDSN.c_str());
718 }
719
720 return DB_SUCCESS;
721}
722
723int SqlODBC::GetNumRows()
724{
725 SQLLEN nrows = 0;
726 /* How many rows are there */
727 int status = SQLRowCount(fStmt, &nrows);
728 if (!SQL_SUCCEEDED(status)) {
729 ReportErrors("SqlODBC::GetNumRow", "SQLRowCount()", status);
730 return -1;
731 }
732 return nrows;
733}
734
735int SqlODBC::GetNumColumns()
736{
737 SQLSMALLINT ncols = 0;
738 /* How many columns are there */
740 if (!SQL_SUCCEEDED(status)) {
741 ReportErrors("SqlODBC::GetNumColumns", "SQLNumResultCols()", status);
742 return -1;
743 }
744 return ncols;
745}
746
747int SqlODBC::Fetch()
748{
749 int status = SQLFetch(fStmt);
750
751 if (status == SQL_NO_DATA)
752 return DB_NO_MORE_SUBKEYS;
753
754 if (!SQL_SUCCEEDED(status)) {
755 ReportErrors("SqlODBC::Fetch", "SQLFetch()", status);
756 return DB_FILE_ERROR;
757 }
758
759 return DB_SUCCESS;
760}
761
762int SqlODBC::Done()
763{
765 if (!SQL_SUCCEEDED(status)) {
766 ReportErrors("SqlODBC::Done", "SQLCloseCursor()", status);
767 return DB_FILE_ERROR;
768 }
769 return DB_SUCCESS;
770}
771
772const char* SqlODBC::GetColumn(int icol)
773{
774 static char buf[1024];
776 int status = SQLGetData(fStmt, icol, SQL_C_CHAR, buf, sizeof(buf), &indicator);
777
778 if (!SQL_SUCCEEDED(status)) {
779 return NULL;
780 }
781
783 return NULL;
784
785 return buf;
786}
787
788#endif
789
791// Done with SQL stuff //
793
795// Data structures to keep track of Events and Tags //
797
798struct Tag
799{
800 std::string column_name;
803 bool create;
804};
805
806struct Event
807{
808 std::string event_name;
809 std::string table_name;
810 std::vector<Tag> tags;
811 bool active;
812
813 Event() // ctor
814 {
815 active = false;
816 }
817
818 ~Event() // dtor
819 {
820 active = false;
821 }
822};
823
824static void PrintTags(int ntags, const TAG tags[])
825{
826 for (int i=0; i<ntags; i++)
827 printf("tag %d: %s %s[%d]\n", i, midasTypeName(tags[i].type), tags[i].name, tags[i].n_data);
828}
829
830int WriteEvent(SqlBase* sql, Event *e, time_t t, const char*buf, int size)
831{
832 //printf("event %d, time %s", rec.event_id, ctime(&t));
833
834 int n = e->tags.size();
835
836 std::string tags;
837 std::string values;
838
839 //if (n>0)
840 // printf(" %s", ctime(&t));
841
842 for (int i=0; i<n; i++) {
843 const Tag*t = &e->tags[i];
844
845 if (t) {
846 int offset = t->offset;
847 void* ptr = (void*)(buf+offset);
848
849 int arraySize = t->tag.n_data;
850
851 for (int j=0; j<arraySize; j++) {
852 tags += ", ";
853 values += ", ";
854
855 if (arraySize <= 1)
856 tags += t->column_name;
857 else {
858 tags += t->column_name;
859 char s[256];
860 sprintf(s,"_%d", j);
861 tags += s;
862 }
863
864 char s[1024];
865
866 switch (t->tag.type) {
867 default:
868 sprintf(s, "unknownType%d", t->tag.type);
869 break;
870 case 1: /* BYTE */
871 sprintf(s, "%u",((unsigned char*)ptr)[j]);
872 break;
873 case 2: /* SBYTE */
874 sprintf(s, "%d",((signed char*)ptr)[j]);
875 break;
876 case 3: /* CHAR */
877 sprintf(s, "\'%c\'",((char*)ptr)[j]);
878 break;
879 case 4: /* WORD */
880 sprintf(s, "%u",((unsigned short*)ptr)[j]);
881 break;
882 case 5: /* SHORT */
883 sprintf(s, "%d",((signed short*)ptr)[j]);
884 break;
885 case 6: /* DWORD */
886 sprintf(s, "%u",((unsigned int*)ptr)[j]);
887 break;
888 case 7: /* INT */
889 sprintf(s, "%d",((int*)ptr)[j]);
890 break;
891 case 8: /* BOOL */
892 sprintf(s, "%u",((unsigned int*)ptr)[j]);
893 break;
894 case 9: /* FLOAT */
895 sprintf(s, "\'%.8g\'",((float*)ptr)[j]);
896 break;
897 case 10: /* DOUBLE */
898 sprintf(s, "\'%.16g\'",((double*)ptr)[j]);
899 break;
900 }
901
902 values += s;
903 }
904 }
905 }
906
907 // 2001-02-16 20:38:40.1
908 struct tm tms;
909 localtime_r(&t, &tms); // somebody must call tzset() before this
910 char s[1024];
911 strftime(s,sizeof(s)-1,"%Y-%m-%d %H:%M:%S.0",&tms);
912
913 char sss[102400];
914 sprintf(sss, "INSERT INTO %s (_t_time, _i_time%s) VALUES (\'%s\', \'%d\'%s);",
915 e->table_name.c_str(),
916 tags.c_str(),
917 s,
918 (int)t,
919 values.c_str());
920
921 int status = sql->Exec(sss);
922
923 if (status != DB_SUCCESS) {
924 return status;
925 }
926
927 return HS_SUCCESS;
928}
929
930// convert MIDAS names to SQL names
931
932static std::string MidasNameToSqlName(const char* s)
933{
934 std::string out;
935
936 for (int i=0; s[i]!=0; i++) {
937 char c = s[i];
938 if (isalpha(c) || isdigit(c))
939 out += tolower(c);
940 else
941 out += '_';
942 }
943
944 return out;
945}
946
948{
949 std::string tag_name;
950 std::string column_name;
952};
953
955{
956 std::string event_name;
957 std::string table_name;
959 std::vector<IndexEntryTag> tags;
960 std::vector<TAG> tags_cache;
961};
962
963static std::vector<IndexEntry*> gHistoryIndex;
964
965#if 0
966static void PrintIndex()
967{
968 for (unsigned i=0; i<gHistoryIndex.size(); i++) {
970
971 printf("entry %d: [%s] [%s], time %d, tags\n", i, e->event_name.c_str(), e->table_name.c_str(), e->timestamp);
972
973 for (unsigned j=0; j<e->tags.size(); j++)
974 printf(" tag %d: [%s] [%s], time %d\n", j, e->tags[j].tag_name.c_str(), e->tags[j].column_name.c_str(), e->tags[j].timestamp);
975 }
976}
977#endif
978
979static IndexEntry* FindIndexByTableName(const char* table_name)
980{
981 for (unsigned i=0; i<gHistoryIndex.size(); i++)
982 if (equal_ustring(gHistoryIndex[i]->table_name.c_str(), table_name)) {
983 return gHistoryIndex[i];
984 }
985 return NULL;
986}
987
988static IndexEntry* FindIndexByEventName(const char* event_name)
989{
990 for (unsigned i=0; i<gHistoryIndex.size(); i++)
991 if (equal_ustring(gHistoryIndex[i]->event_name.c_str(), event_name)) {
992 return gHistoryIndex[i];
993 }
994 return NULL;
995}
996
997static IndexEntryTag* FindIndexByTagName(IndexEntry* ie, const char* tag_name)
998{
999 for (unsigned i=0; i<ie->tags.size(); i++)
1000 if (equal_ustring(ie->tags[i].tag_name.c_str(), tag_name)) {
1001 return &ie->tags[i];
1002 }
1003 return NULL;
1004}
1005
1006static IndexEntryTag* FindIndexByColumnName(IndexEntry* ie, const char* column_name)
1007{
1008 for (unsigned i=0; i<ie->tags.size(); i++)
1009 if (equal_ustring(ie->tags[i].column_name.c_str(), column_name)) {
1010 return &ie->tags[i];
1011 }
1012 return NULL;
1013}
1014
1015static int gHaveIndex = true;
1016static int gHaveIndexAll = false;
1017
1018static int gTrace = 0;
1019
1020static int ReadIndex(SqlBase* sql, const char* event_name)
1021{
1022 if (gTrace)
1023 printf("ReadIndex [%s]\n", event_name);
1024
1025 if (!gHaveIndex)
1026 return HS_FILE_ERROR;
1027
1028 if (gHaveIndexAll)
1029 return HS_SUCCESS;
1030
1031 if (gTrace)
1032 printf("ReadIndex: reading index for event [%s]\n", event_name);
1033
1034 //event_name = NULL;
1035
1036 char cmd[256];
1037
1038 if (event_name)
1039 sprintf(cmd, "SELECT event_name, table_name, tag_name, column_name, itimestamp FROM _history_index where event_name=\'%s\';", event_name);
1040 else
1041 sprintf(cmd, "SELECT event_name, table_name, tag_name, column_name, itimestamp FROM _history_index;");
1042
1043 int status = sql->Exec(cmd);
1044
1045 if (status == DB_NO_KEY) {
1046 gHaveIndex = false;
1047 return HS_FILE_ERROR;
1048 }
1049
1050 if (gTrace) {
1051 printf("ReadIndex: event %s, Read status %d, nrows: %d\n",
1052 event_name,
1053 status,
1054 sql->GetNumRows());
1055 }
1056
1057 if (status != SUCCESS)
1058 return HS_FILE_ERROR;
1059
1060 if (sql->GetNumRows() == 0) {
1061 sql->Done();
1062 return HS_FILE_ERROR;
1063 }
1064
1065 int nrows = sql->GetNumRows();
1066 int ncols = sql->GetNumColumns();
1067
1068 if (nrows == 0)
1069 return HS_SUCCESS;
1070
1071 if (gTrace)
1072 printf("ReadIndex: event %s, nrows: %d, ncols: %d\n",
1073 event_name,
1074 nrows, ncols);
1075
1076 if (nrows < 0)
1077 return HS_FILE_ERROR;
1078
1079 if (ncols < 1)
1080 return HS_FILE_ERROR;
1081
1082 /* Loop through the rows in the result-set */
1083 while (1) {
1084 status = sql->Fetch();
1085 if (status != DB_SUCCESS)
1086 break;
1087
1088 std::string xevent_name = sql->GetColumn(1);
1089
1090 const char* p = sql->GetColumn(2);
1091 if (p) { // table declaration
1092 std::string xtable_name = p;
1093 std::string xtimestamp = sql->GetColumn(5);
1094 int timestamp = atoi(xtimestamp.c_str());
1095
1097 if (!ie) {
1098 ie = new IndexEntry;
1099 gHistoryIndex.push_back(ie);
1100 ie->timestamp = timestamp - 1; // make sure we update this entry
1101 }
1102
1103 if (timestamp > ie->timestamp) {
1104 ie->event_name = xevent_name;
1105 ie->table_name = xtable_name;
1106 ie->timestamp = timestamp;
1107 }
1108
1109 //printf("%s %s %s %s %s [%s]\n", xevent_name.c_str(), xtable_name.c_str(), "???", "???", xtimestamp.c_str(), p);
1110 continue;
1111 }
1112
1113 p = sql->GetColumn(3);
1114 if (p) { // tag declaration
1115 std::string xtag_name = p;
1116 std::string xcolumn_name = sql->GetColumn(4);
1117 std::string xtimestamp = sql->GetColumn(5);
1118 int timestamp = atoi(xtimestamp.c_str());
1119
1121 if (!ie) {
1122 ie = new IndexEntry;
1123 gHistoryIndex.push_back(ie);
1124 ie->timestamp = 0;
1125 ie->event_name = xevent_name;
1126 }
1127
1128 bool found = false;
1129 for (unsigned j=0; j<ie->tags.size(); j++)
1130 if (ie->tags[j].tag_name == xtag_name) {
1131 if (timestamp > ie->tags[j].timestamp) {
1132 ie->tags[j].timestamp = timestamp;
1133 ie->tags[j].column_name = xcolumn_name;
1134 }
1135 found = true;
1136 break;
1137 }
1138
1139 if (!found) {
1142 it.column_name = xcolumn_name;
1143 it.timestamp = timestamp;
1144 ie->tags.push_back(it);
1145 }
1146
1147 //printf("%s %s %s %s %s\n", xevent_name.c_str(), "???", xtag_name.c_str(), xcolumn_name.c_str(), xtimestamp.c_str());
1148 continue;
1149 }
1150
1151 }
1152
1153 sql->Done();
1154
1155 gHaveIndex = true;
1156
1157 if (event_name == NULL)
1158 gHaveIndexAll = true;
1159
1160 //PrintIndex();
1161
1162 return HS_SUCCESS;
1163}
1164
1166// Implementation of the MidasHistoryInterface //
1168
1170{
1171public:
1174 std::string fConnectString;
1177 std::vector<Event*> fEvents;
1178 std::vector<std::string> fIndexEvents;
1181
1183 {
1184 fDebug = 0;
1185 fConnectRetry = 0;
1186 fNextConnect = 0;
1187 fSql = b;
1188 fHaveIndex = false;
1189 fHaveXIndex = false;
1190 }
1191
1193 {
1194 hs_disconnect();
1195 delete fSql;
1196 fSql = NULL;
1197 }
1198
1200 {
1201 int old = fDebug;
1202 fDebug = debug;
1203 gTrace = debug;
1205 return old;
1206 }
1207
1209 {
1210 if (fDebug)
1211 printf("hs_connect %s!\n", connect_string);
1212
1213 assert(fSql);
1214
1215 if (fSql->IsConnected())
1216 if (strcmp(fConnectString.c_str(), connect_string) == 0)
1217 return HS_SUCCESS;
1218
1219 hs_disconnect();
1220
1222
1223 if (fDebug)
1224 printf("hs_connect: connecting to SQL database \'%s\'\n", fConnectString.c_str());
1225
1226 int status = fSql->Connect(fConnectString.c_str());
1227 if (status != DB_SUCCESS)
1228 return status;
1229
1230 std::vector<std::string> tables;
1231
1233 if (status != DB_SUCCESS)
1234 return status;
1235
1236 for (unsigned i=0; i<tables.size(); i++) {
1237 if (tables[i] == "_history_index") {
1238 fHaveIndex = true;
1239 break;
1240 }
1241 }
1242
1243 return HS_SUCCESS;
1244 }
1245
1247 {
1248 if (fDebug)
1249 printf("hs_disconnect!\n");
1250
1251 fSql->Disconnect();
1252
1254
1255 return HS_SUCCESS;
1256 }
1257
1259 {
1260 if (fDebug)
1261 printf("Reconnect to SQL database!\n");
1262
1263 fSql->Disconnect();
1264 fSql->Connect(fConnectString.c_str());
1265 if (!fSql->IsConnected()) {
1266 return HS_FILE_ERROR;
1267 }
1268
1269 return HS_SUCCESS;
1270 }
1271
1273 // Internal data caches //
1275
1277 {
1278 if (fDebug)
1279 printf("hs_clear_cache!\n");
1280
1281 gHaveIndex = true;
1282 gHaveIndexAll = false;
1283 fHaveXIndex = false;
1284
1285 for (unsigned i=0; i<gHistoryIndex.size(); i++) {
1287 delete ie;
1288 }
1289 gHistoryIndex.clear();
1290
1291 fIndexEvents.clear();
1292
1293 return HS_SUCCESS;
1294 }
1295
1297 {
1298 if (fHaveXIndex)
1299 return HS_SUCCESS;
1300
1301 if (fDebug)
1302 printf("XReadIndex!\n");
1303
1304 std::vector<std::string> tables;
1305
1306 int status = fSql->ListTables(&tables);
1307 if (status != DB_SUCCESS)
1308 return status;
1309
1310 for (unsigned i=0; i<tables.size(); i++) {
1311 if (tables[i] == "_history_index")
1312 continue;
1313
1314 IndexEntry* ie = NULL; //FindEventName(tables[i].c_str());
1315
1316 if (!ie) {
1317 ie = new IndexEntry;
1318
1319 ie->table_name = tables[i];
1320 ie->event_name = ie->table_name;
1321
1322 gHistoryIndex.push_back(ie);
1323 }
1324
1325 std::vector<std::string> columns;
1326
1327 status = fSql->ListColumns(ie->table_name.c_str(), &columns);
1328 if (status != DB_SUCCESS)
1329 return status;
1330
1331 for (unsigned int j=0; j<columns.size(); j+=2) {
1332 if (columns[j] == "_t_time")
1333 continue;
1334 if (columns[j] == "_i_time")
1335 continue;
1336
1337 IndexEntryTag t;
1338 t.column_name = columns[j];
1339 t.tag_name = t.column_name;
1340 t.timestamp = 0;
1341
1342 ie->tags.push_back(t);
1343 }
1344 }
1345
1346 fHaveXIndex = true;
1347
1348 //PrintIndex();
1349
1350 return HS_SUCCESS;
1351 }
1352
1354 // Functions used by mlogger //
1356
1357 int hs_define_event(const char* event_name, time_t timestamp, int ntags, const TAG tags[])
1358 {
1359 int status;
1360
1361 if (fDebug) {
1362 printf("define event [%s] with %d tags:\n", event_name, ntags);
1363 PrintTags(ntags, tags);
1364 }
1365
1366 // delete all events with the same name
1367 for (unsigned int i=0; i<fEvents.size(); i++)
1368 if (fEvents[i])
1369 if (fEvents[i]->event_name == event_name) {
1370 if (fDebug)
1371 printf("deleting exising event %s\n", event_name);
1372 delete fEvents[i];
1373 fEvents[i] = NULL;
1374 }
1375
1376 Event* e = new Event();
1377
1378 e->event_name = event_name;
1379
1380 if (!fHaveIndex) {
1381 char buf[1024];
1382 sprintf(buf, "CREATE TABLE _history_index (event_name VARCHAR(256) NOT NULL, table_name VARCHAR(256), tag_name VARCHAR(256), column_name VARCHAR(256), itimestamp INTEGER NOT NULL);");
1383 int status = fSql->Exec(buf);
1384 if (status == DB_KEY_EXIST)
1385 /* do nothing */ ;
1386 else if (status != DB_SUCCESS)
1387 return status;
1388 fHaveIndex = true;
1389 }
1390
1391 IndexEntry* ie = FindIndexByEventName(event_name);
1392
1393 if (!ie) {
1394 ReadIndex(fSql, event_name);
1395 ie = FindIndexByEventName(event_name);
1396 }
1397
1398 if (!ie) {
1399 std::string table_name = MidasNameToSqlName(event_name);
1400
1401 char sss[102400];
1402 sprintf(sss, "INSERT INTO _history_index (event_name, table_name, itimestamp) VALUES (\'%s\', \'%s\', \'%.0f\');",
1403 event_name,
1404 table_name.c_str(),
1405 (double)timestamp);
1406
1407 int status = fSql->Exec(sss);
1408 if (status != DB_SUCCESS)
1409 return HS_FILE_ERROR;
1410
1411 ReadIndex(fSql, event_name);
1412 ie = FindIndexByEventName(event_name);
1413 }
1414
1415 if (!ie) {
1416 cm_msg(MERROR, "hs_define_event", "could not add event name to SQL history index table, see messages");
1417 return HS_FILE_ERROR;
1418 }
1419
1420 e->table_name = ie->table_name;
1421 e->active = true;
1422
1423 bool create_event = false;
1424
1425 int offset = 0;
1426 for (int i=0; i<ntags; i++) {
1427 for (unsigned int j=0; j<tags[i].n_data; j++) {
1428 std::string tagname = tags[i].name;
1429 std::string colname = MidasNameToSqlName(tags[i].name);
1430
1431 if (tags[i].n_data > 1) {
1432 char s[256];
1433 sprintf(s, "[%d]", j);
1434 tagname += s;
1435
1436 sprintf(s, "_%d", j);
1437 colname += s;
1438 }
1439
1441 if (!it) {
1442 // check for duplicate names
1443
1444 while (1) {
1445 bool dupe = false;
1446
1447 for (unsigned i=0; i<e->tags.size(); i++) {
1448 if (colname == e->tags[i].column_name) {
1449 //printf("duplicate name %s\n", colname.c_str());
1450 dupe = true;
1451 break;
1452 }
1453 }
1454
1455 if (!dupe)
1456 break;
1457
1458 char s[256];
1459 sprintf(s, "_%d", rand());
1460 colname += s;
1461 }
1462
1463 // add tag name to column name translation to the history index
1464
1465 char sss[102400];
1466 sprintf(sss, "INSERT INTO _history_index (event_name, tag_name, column_name, itimestamp) VALUES (\'%s\', \'%s\', \'%s\', \'%.0f\');",
1467 event_name,
1468 tagname.c_str(),
1469 colname.c_str(),
1470 (double)timestamp);
1471
1472 int status = fSql->Exec(sss);
1473 if (status != DB_SUCCESS)
1474 return HS_FILE_ERROR;
1475
1476 // reload the history index
1477
1478 ReadIndex(fSql, event_name);
1479 ie = FindIndexByEventName(event_name);
1480 assert(ie);
1481 it = FindIndexByTagName(ie, tagname.c_str());
1482 }
1483
1484 if (!it) {
1485 cm_msg(MERROR, "hs_define_event", "could not add event tags to SQL history index table, see messages");
1486 return HS_FILE_ERROR;
1487 }
1488
1489 Tag t;
1490 t.column_name = it->column_name;
1491 t.create = false;
1492 t.offset = offset;
1493 t.tag = tags[i];
1494 t.tag.n_data = 1;
1495 e->tags.push_back(t);
1496 int size = tid_size[tags[i].type];
1497 offset += size;
1498 }
1499 }
1500
1501 std::vector<std::string> columns;
1502
1503 status = fSql->ListColumns(e->table_name.c_str(), &columns);
1504 if (status != DB_SUCCESS)
1505 return status;
1506
1507 if (columns.size() <= 0)
1508 create_event = true;
1509
1510 for (unsigned i=0; i<e->tags.size(); i++) {
1511 // check for duplicate column names
1512 for (unsigned j=i+1; j<e->tags.size(); j++)
1513 if (e->tags[i].column_name == e->tags[j].column_name) {
1514 cm_msg(MERROR, "hs_define_event", "Error: History event \'%s\': Duplicated column name \'%s\' from tags %d \'%s\' and %d \'%s\'", event_name, e->tags[i].column_name.c_str(), i, e->tags[i].tag.name, j, e->tags[j].tag.name);
1515 e->active = false;
1516 break;
1517 }
1518
1519 // check if new column needs to be created
1520 bool found = false;
1521 for (size_t j=0; j<columns.size(); j+=2) {
1522 if (e->tags[i].column_name == columns[j]) {
1523 // column exists, check data type
1524 //printf("column \'%s\', data type %s\n", e->tags[i].column_name.c_str(), columns[j+1].c_str());
1525
1526 if (!isCompatible(e->tags[i].tag.type, columns[j+1].c_str())) {
1527 cm_msg(MERROR, "hs_define_event", "Error: History event \'%s\': Incompatible data type for tag \'%s\' type \'%s\', SQL column \'%s\' type \'%s\'", event_name, e->tags[i].tag.name, midasTypeName(e->tags[i].tag.type), columns[j].c_str(), columns[j+1].c_str());
1528 e->active = false;
1529 }
1530
1531 found = true;
1532 break;
1533 }
1534 }
1535
1536 if (!found) {
1537 // create it
1538 //printf("column \'%s\', data type %s --- create!\n", e->tags[i].column_name.c_str(), midasTypeName(e->tags[i].tag.type));
1539 e->tags[i].create = true;
1540 }
1541 }
1542
1543 if (create_event) {
1544 char buf[1024];
1545 sprintf(buf, "CREATE TABLE %s (_t_time TIMESTAMP NOT NULL, _i_time INTEGER NOT NULL, INDEX (_i_time), INDEX (_t_time));", e->table_name.c_str());
1546 status = fSql->Exec(buf);
1547 if (status != DB_SUCCESS) {
1548 e->active = false;
1549 return HS_FILE_ERROR;
1550 }
1551 }
1552
1553 for (size_t i=0; i<e->tags.size(); i++)
1554 if (e->tags[i].create) {
1555 char buf[1024];
1556
1557 sprintf(buf, "ALTER TABLE %s ADD COLUMN %s %s;",
1558 e->table_name.c_str(),
1559 e->tags[i].column_name.c_str(),
1560 midas2sqlType(e->tags[i].tag.type));
1561
1562 status = fSql->Exec(buf);
1563
1564 if (status != DB_SUCCESS) {
1565 e->active = false;
1566 return HS_FILE_ERROR;
1567 }
1568 }
1569
1570 // find empty slot in events list
1571 for (unsigned int i=0; i<fEvents.size(); i++)
1572 if (!fEvents[i]) {
1573 fEvents[i] = e;
1574 e = NULL;
1575 break;
1576 }
1577
1578 // if no empty slots, add at the end
1579 if (e)
1580 fEvents.push_back(e);
1581
1582 return HS_SUCCESS;
1583 }
1584
1585 int hs_write_event(const char* event_name, time_t timestamp, int buffer_size, const char* buffer)
1586 {
1587 if (fDebug)
1588 printf("hs_write_event: write event \'%s\', time %d, size %d\n", event_name, (int)timestamp, buffer_size);
1589
1590 // if disconnected, try to reconnect
1591
1592 if (!fSql->IsConnected()) {
1593 time_t now = time(NULL);
1594
1595 // too early to try reconnecting?
1596 if (fConnectRetry !=0 && now < fNextConnect) {
1597 return HS_FILE_ERROR;
1598 }
1599
1600 cm_msg(MINFO, "hs_write_event", "Trying to reconnect to SQL database \'%s\'", fConnectString.c_str());
1601
1602 int status = fSql->Connect(fConnectString.c_str());
1603
1604 if (status != DB_SUCCESS) {
1605
1606 // first retry in 5 seconds
1607 if (fConnectRetry == 0)
1608 fConnectRetry = 5;
1609
1611
1612 // exponential backoff
1613 fConnectRetry *= 2;
1614
1615 // but no more than every 10 minutes
1616 if (fConnectRetry > 10*60)
1617 fConnectRetry = 10*60;
1618
1619 return HS_FILE_ERROR;
1620 }
1621
1622 cm_msg(MINFO, "hs_write_event", "Reconnected to SQL database \'%s\'", fConnectString.c_str());
1623 }
1624
1625 fNextConnect = 0;
1626 fConnectRetry = 0;
1627
1628 Event *e = NULL;
1629
1630 // find this event
1631 for (size_t i=0; i<fEvents.size(); i++)
1632 if (fEvents[i]->event_name == event_name) {
1633 e = fEvents[i];
1634 break;
1635 }
1636
1637 // not found
1638 if (!e)
1639 return HS_UNDEFINED_EVENT;
1640
1641 // deactivated because of error?
1642 if (!e->active)
1643 return HS_FILE_ERROR;
1644
1645 int status = WriteEvent(fSql, e, timestamp, buffer, buffer_size);
1646
1647 // if could not write to SQL?
1648 if (status != HS_SUCCESS) {
1649
1650 // if lost SQL connection, try again later
1651
1652 if (!fSql->IsConnected()) {
1653 return HS_FILE_ERROR;
1654 }
1655
1656 // otherwise, deactivate this event
1657
1658 e->active = false;
1659
1660 cm_msg(MERROR, "hs_write_event", "Event \'%s\' disabled after write error %d into SQL database \'%s\'", event_name, status, fConnectString.c_str());
1661
1662 return HS_FILE_ERROR;
1663 }
1664
1665 return HS_SUCCESS;
1666 }
1667
1669 {
1670 if (fDebug)
1671 printf("hs_flush_buffers!\n");
1672 return HS_SUCCESS;
1673 }
1674
1676 // Functions used by mhttpd //
1678
1679 int hs_get_events(time_t t, std::vector<std::string> *pevents)
1680 {
1681 if (fDebug)
1682 printf("hs_get_events!\n");
1683
1684 if (fIndexEvents.size() == 0) {
1685
1686 if (fDebug)
1687 printf("hs_get_events: reading event names!\n");
1688
1690
1691 std::vector<std::string> tables;
1692 int status = fSql->ListTables(&tables);
1693 if (status != DB_SUCCESS)
1694 return status;
1695
1696 for (unsigned i=0; i<tables.size(); i++) {
1697 if (tables[i] == "_history_index")
1698 continue;
1699
1701 if (!ie) {
1704 }
1705
1706 if (ie)
1707 fIndexEvents.push_back(ie->event_name);
1708 else
1709 fIndexEvents.push_back(tables[i]);
1710 }
1711 }
1712
1713 assert(pevents);
1715
1716 return HS_SUCCESS;
1717 }
1718
1719 int hs_get_tags(const char* event_name, time_t t, std::vector<TAG> *ptags)
1720 {
1721 if (fDebug)
1722 printf("hs_get_tags for [%s]\n", event_name);
1723
1724 assert(ptags);
1725
1726 IndexEntry* ie = FindIndexByEventName(event_name);
1727
1728 if (!ie) {
1729 ReadIndex(fSql, event_name);
1730 ie = FindIndexByEventName(event_name);
1731 }
1732
1733 if (!ie) {
1734 XReadIndex();
1735 ie = FindIndexByEventName(event_name);
1736 }
1737
1738 if (!ie)
1739 return HS_UNDEFINED_EVENT;
1740
1741 if (ie->tags_cache.size() == 0) {
1742 if (fDebug)
1743 printf("hs_get_tags reading tags for [%s]\n", event_name);
1744
1745 std::string tname = ie->table_name;
1746
1747 std::vector<std::string> columns;
1748
1749 int status = fSql->ListColumns(tname.c_str(), &columns);
1750 if (status != DB_SUCCESS)
1751 return status;
1752
1753 if (columns.size() < 1) {
1754 cm_msg(MERROR, "hs_get_tags", "Cannot get columns for table \'%s\', try to reconnect to the database", tname.c_str());
1755
1756 int status = Reconnect();
1757 if (status != HS_SUCCESS)
1758 return status;
1759
1760 columns.clear();
1761 status = fSql->ListColumns(tname.c_str(), &columns);
1762 if (status != DB_SUCCESS)
1763 return status;
1764 }
1765
1766 //TAG* t = (TAG*)malloc(sizeof(TAG)*columns.size());
1767 //assert(t);
1768
1769 for (unsigned int j=0; j<columns.size(); j+=2) {
1770 if (columns[j] == "_t_time")
1771 continue;
1772 if (columns[j] == "_i_time")
1773 continue;
1774
1776
1777 TAG t;
1778 if (it)
1779 mstrlcpy(t.name, it->tag_name.c_str(), sizeof(t.name));
1780 else
1781 mstrlcpy(t.name, columns[j].c_str(), sizeof(t.name));
1782 t.type = sql2midasType(columns[j+1].c_str());
1783 t.n_data = 1;
1784
1785 ie->tags_cache.push_back(t);
1786 }
1787 }
1788
1789 for (unsigned i=0; i<ie->tags_cache.size(); i++)
1790 ptags->push_back(ie->tags_cache[i]);
1791
1792 return HS_SUCCESS;
1793 }
1794
1795 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[])
1796 {
1797 for (int i=0; i<num_var; i++)
1798 last_written[i] = 0;
1799 return HS_FILE_ERROR;
1800 }
1801
1802 int hs_read_old_style(double start_time, double end_time, double interval,
1803 const char* event_name, const char* tag_name, int var_index,
1804 int *num_entries,
1805 time_t** time_buffer, double**data_buffer)
1806 {
1807 if (fDebug) {
1808 printf("hs_read_old_style: event \"%s\", tag \"%s\"\n", event_name, tag_name);
1809 }
1810
1812
1813 for (unsigned e=0; e<gHistoryIndex.size(); e++) {
1814
1815 const char* s = gHistoryIndex[e]->event_name.c_str();
1816
1817 bool match = false;
1818 for (int j=0; s[j]; j++) {
1819
1820 if ((event_name[j]==0) && (s[j]=='/')) {
1821 match = true;
1822 break;
1823 }
1824
1825 if ((event_name[j]==0) && (s[j]=='_')) {
1826 match = true;
1827 break;
1828 }
1829
1830 if (event_name[j]==0) {
1831 match = false;
1832 break;
1833 }
1834
1835 if (tolower(event_name[j]) != tolower(s[j])) {
1836 match = false;
1837 break;
1838 }
1839 }
1840
1841 //printf("try %s, match %d\n", s, match);
1842
1843 if (match) {
1844 bool found_tag = false;
1846 for (unsigned v=0; v<ie->tags.size(); v++) {
1847 //printf("try tag [%s] looking for [%s]\n", ie->tags[v].tag_name.c_str(), tag_name);
1848 if (equal_ustring(tag_name, ie->tags[v].tag_name.c_str())) {
1849 found_tag = true;
1850 break;
1851 }
1852 }
1853
1854 if (!found_tag)
1855 match = false;
1856 }
1857
1858 if (match) {
1859 if (fDebug)
1860 printf("hs_read_old_style: event \"%s\", tag \"%s\", try matching event \'%s\'\n", event_name, tag_name, s);
1861
1862 int status = hs_read(start_time, end_time, interval,
1863 s, tag_name, var_index,
1864 num_entries,
1866
1867 if (status==HS_SUCCESS && *num_entries>0)
1868 return HS_SUCCESS;
1869 }
1870 }
1871
1872 return HS_UNDEFINED_VAR;
1873 }
1874
1875 int hs_read(double start_time, double end_time, double interval,
1876 const char* event_name, const char* tag_name, int tag_index,
1877 int *num_entries,
1878 time_t** time_buffer, double**data_buffer)
1879 {
1880 *num_entries = 0;
1881 *time_buffer = NULL;
1882 *data_buffer = NULL;
1883
1884 if (fDebug)
1885 printf("hs_read: event [%s], tag [%s], index %d, start %f, end %f, dt %f, interval %f, max points %f\n",
1886 event_name, tag_name, tag_index,
1887 start_time, end_time, end_time-start_time, interval, (end_time-start_time)/interval);
1888
1889 if (event_name==NULL)
1890 return HS_SUCCESS;
1891
1892 IndexEntry*ie = FindIndexByEventName(event_name);
1893
1894 if (!ie) {
1895 ReadIndex(fSql, event_name);
1896 ie = FindIndexByEventName(event_name);
1897 }
1898
1899 if (!ie) {
1900 XReadIndex();
1901 ie = FindIndexByEventName(event_name);
1902 }
1903
1905
1906 if (ie)
1907 it = FindIndexByTagName(ie, tag_name);
1908
1909 if (ie && !it) { // maybe this is an array without "Names"?
1910 char xxx[256];
1911 sprintf(xxx, "%s[%d]", tag_name, tag_index);
1913 }
1914
1915 // new-style event name: "equipment_name/variable_name:tag_name"
1916 // old style event name: "equipment_name:tag_name" ("variable_name" is missing)
1917 bool oldStyleEventName = (strchr(event_name, '/')==NULL);
1918
1920 if (!ie || !it) {
1921 return hs_read_old_style(start_time, end_time, interval,
1922 event_name, tag_name, tag_index,
1923 num_entries,
1925 }
1926
1927 if (!it)
1928 return HS_UNDEFINED_VAR;
1929
1930 assert(ie);
1931 assert(it);
1932
1933 std::string tname = ie->table_name;
1934 std::string cname = it->column_name;
1935
1936 char cmd[256];
1937 sprintf(cmd, "SELECT _i_time, %s FROM %s WHERE _i_time>=%.0f and _i_time<=%.0f ORDER BY _i_time;",
1938 cname.c_str(), tname.c_str(),
1939 start_time, end_time);
1940
1941 int status = fSql->Exec(cmd);
1942
1943 if (fDebug) {
1944 printf("hs_read: event \"%s\", tag \"%s\", index %d: Read table \"%s\" column \"%s\": status %d, nrows: %d, ncolumns: %d\n",
1945 event_name, tag_name, tag_index,
1946 tname.c_str(),
1947 cname.c_str(),
1948 status,
1949 fSql->GetNumRows(),
1951 );
1952 }
1953
1954 if (status != SUCCESS) {
1955 return HS_FILE_ERROR;
1956 }
1957
1958 if (fSql->GetNumRows() == 0) {
1959 fSql->Done();
1960
1961 if (oldStyleEventName) {
1962 return hs_read_old_style(start_time, end_time, interval,
1963 event_name, tag_name, tag_index,
1964 num_entries,
1966 }
1967
1968 return HS_SUCCESS;
1969 }
1970
1971 int nrows = fSql->GetNumRows();
1972 int ncols = fSql->GetNumColumns();
1973
1974 if (nrows < 0)
1975 return HS_FILE_ERROR;
1976
1977 if (ncols < 1)
1978 return HS_FILE_ERROR;
1979
1980 *num_entries = 0;
1981 *time_buffer = (time_t*)malloc(nrows * sizeof(time_t));
1982 *data_buffer = (double*)malloc(nrows * sizeof(double));
1983
1984 /* Loop through the rows in the result-set */
1985 int row = 0;
1986 time_t tt = 0;
1987 int ann = 0;
1988 double att = 0;
1989 double avv = 0;
1990 while (1) {
1991 status = fSql->Fetch();
1992 if (status != DB_SUCCESS)
1993 break;
1994
1995 time_t t = 0;
1996 double v = 0;
1997
1998 const char* timedata = fSql->GetColumn(1);
1999 if (timedata)
2000 t = atoi(timedata);
2001
2002 const char* valuedata = fSql->GetColumn(2);
2003 if (valuedata)
2004 v = atof(valuedata);
2005
2006 if (t < start_time || t > end_time)
2007 continue;
2008
2009 //printf("Row %d, time %d, value %f\n", row, t, v);
2010 //printf("tt: %d, ann: %d\n", tt, ann);
2011
2012 if (tt == 0 || t >= tt + interval) {
2013
2014 if (ann > 0) {
2015 assert(row < nrows);
2016
2017 (*time_buffer)[row] = (time_t)(att/ann);
2018 (*data_buffer)[row] = avv/ann;
2019
2020 row++;
2021 (*num_entries) = row;
2022 }
2023
2024 ann = 0;
2025 att = 0;
2026 avv = 0;
2027 tt = t;
2028
2029 }
2030
2031 ann++;
2032 att += t;
2033 avv += v;
2034 }
2035
2036 if (ann > 0) {
2037 assert(row < nrows);
2038
2039 (*time_buffer)[row] = (time_t)(att/ann);
2040 (*data_buffer)[row] = avv/ann;
2041
2042 row++;
2043 (*num_entries) = row;
2044 }
2045
2046 fSql->Done();
2047
2048 if (fDebug)
2049 printf("hs_read: return %d entries\n", *num_entries);
2050
2051 return HS_SUCCESS;
2052 }
2053
2054 int hs_read(time_t start_time, time_t end_time, time_t interval,
2055 int num_var,
2056 const char* const event_name[], const char* const tag_name[], const int tag_index[],
2057 int num_entries[],
2058 time_t* time_buffer[], double* data_buffer[],
2059 int st[])
2060 {
2061 if (fDebug)
2062 printf("hs_read: %d variables\n", num_var);
2063
2064 if (!fSql->IsConnected())
2065 return HS_FILE_ERROR;
2066
2067 for (int i=0; i<num_var; i++) {
2068
2069 if (event_name[i]==NULL) {
2071 num_entries[i] = 0;
2072 continue;
2073 }
2074
2075 st[i] = hs_read((double)start_time, (double)end_time, (double)interval,
2076 event_name[i], tag_name[i], tag_index[i],
2077 &num_entries[i],
2078 &time_buffer[i], &data_buffer[i]);
2079 }
2080
2081 return HS_SUCCESS;
2082 }
2083
2084 /*------------------------------------------------------------------*/
2085
2086 int hs_read2(time_t start_time, time_t end_time, time_t interval,
2087 int num_var,
2088 const char* const event_name[], const char* const tag_name[], const int var_index[],
2089 int num_entries[],
2091 double* mean_buffer[],
2092 double* rms_buffer[],
2093 double* min_buffer[],
2094 double* max_buffer[],
2095 int read_status[])
2096 {
2097 int status = hs_read(start_time, end_time, interval, num_var, event_name, tag_name, var_index, num_entries, time_buffer, mean_buffer, read_status);
2098
2099 for (int i=0; i<num_var; i++) {
2100 int num = num_entries[i];
2101 rms_buffer[i] = (double*)malloc(sizeof(double)*num);
2102 min_buffer[i] = (double*)malloc(sizeof(double)*num);
2103 max_buffer[i] = (double*)malloc(sizeof(double)*num);
2104
2105 for (int j=0; j<num; j++) {
2106 rms_buffer[i][j] = 0;
2107 min_buffer[i][j] = mean_buffer[i][j];
2108 max_buffer[i][j] = mean_buffer[i][j];
2109 }
2110 }
2111
2112 return status;
2113 }
2114
2115 /*------------------------------------------------------------------*/
2116
2117 int hs_read_buffer(time_t start_time, time_t end_time,
2118 int num_var, const char* const event_name[], const char* const tag_name[], const int var_index[],
2120 int status[])
2121 {
2122 return HS_FILE_ERROR;
2123 }
2124
2125 /*------------------------------------------------------------------*/
2126
2127 int hs_read_binned(time_t start_time, time_t end_time, int num_bins,
2128 int num_var, const char* const event_name[], const char* const tag_name[], const int var_index[],
2129 int num_entries[],
2130 int* count_bins[], double* mean_bins[], double* rms_bins[], double* min_bins[], double* max_bins[],
2133 time_t last_time[], double last_value[],
2134 int status[])
2135 {
2136 return HS_FILE_ERROR;
2137 }
2138};
2139
2141// Factory constructors //
2143
2144#ifdef HAVE_ODBC
2146{
2147 return new SqlHistory(new SqlODBC());
2148}
2149#else
2154#endif
2155
2160
2161/* emacs
2162 * Local Variables:
2163 * tab-width: 8
2164 * c-basic-offset: 3
2165 * indent-tabs-mode: nil
2166 * End:
2167 */
char name[NAME_LENGTH]
Definition history.h:111
virtual int ListColumns(const char *table, std::vector< std::string > *plist)=0
virtual int Fetch()=0
virtual bool IsConnected()=0
virtual int SetDebug(int debug)=0
virtual const char * GetColumn(int icol)=0
virtual ~SqlBase()
virtual int ListTables(std::vector< std::string > *plist)=0
virtual int Disconnect()=0
virtual int Done()=0
virtual int Exec(const char *sql)=0
virtual int Connect(const char *dsn=0)=0
virtual int GetNumColumns()=0
virtual int GetNumRows()=0
int GetNumColumns()
int GetNumRows()
int Connect(const char *filename=NULL)
int SetDebug(int debug)
int Exec(const char *sql)
int Disconnect()
int ListColumns(const char *table, std::vector< std::string > *plist)
bool fIsConnected
bool IsConnected()
const char * GetColumn(int icol)
int ListTables(std::vector< std::string > *plist)
int hs_read_old_style(double start_time, double end_time, double interval, const char *event_name, const char *tag_name, int var_index, int *num_entries, time_t **time_buffer, double **data_buffer)
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 tag_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 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_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[])
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
SqlBase * fSql
int hs_flush_buffers()
flush buffered data to storage where it is visible to mhttpd
int hs_clear_cache()
clear internal cache, returns HS_SUCCESS
int hs_disconnect()
disconnect from history, returns HS_SUCCESS
int hs_read_buffer(time_t start_time, time_t end_time, int num_var, const char *const event_name[], const char *const tag_name[], const int var_index[], MidasHistoryBufferInterface *buffer[], int status[])
returns HS_SUCCESS
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 tag_name[], const int tag_index[], int num_entries[], time_t *time_buffer[], double *data_buffer[], int st[])
see hs_read(), returns HS_SUCCESS
int hs_connect(const char *connect_string)
returns HS_SUCCESS
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_read(double start_time, double end_time, double interval, const char *event_name, const char *tag_name, int tag_index, int *num_entries, time_t **time_buffer, double **data_buffer)
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_set_debug(int debug)
set debug level, returns previous debug level
int hs_read2(time_t start_time, time_t end_time, time_t interval, int num_var, const char *const event_name[], const char *const tag_name[], const int var_index[], int num_entries[], time_t *time_buffer[], double *mean_buffer[], double *rms_buffer[], double *min_buffer[], double *max_buffer[], int read_status[])
std::vector< Event * > fEvents
SqlHistory(SqlBase *b)
std::string fConnectString
std::vector< std::string > fIndexEvents
#define DB_KEY_EXIST
Definition midas.h:641
#define DB_FILE_ERROR
Definition midas.h:647
#define DB_SUCCESS
Definition midas.h:631
#define DB_NO_KEY
Definition midas.h:642
#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 SUCCESS
Definition mcstd.h:54
#define MINFO
Definition midas.h:560
#define TID_UINT8
Definition midas.h:328
#define MERROR
Definition midas.h:559
#define TID_UINT16
Definition midas.h:333
#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
BOOL equal_ustring(const char *str1, const char *str2)
Definition odb.cxx:3201
static IndexEntry * FindIndexByTableName(const char *table_name)
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 int gHaveIndexAll
static int gTrace
static const char * midasTypeName(int tid)
static IndexEntry * FindIndexByEventName(const char *event_name)
static const char * sql_type_mysql[]
static IndexEntryTag * FindIndexByColumnName(IndexEntry *ie, const char *column_name)
static int gHaveIndex
MidasHistoryInterface * MakeMidasHistoryODBC()
static IndexEntryTag * FindIndexByTagName(IndexEntry *ie, const char *tag_name)
static bool isCompatible(int tid, const char *sqlType)
static const char * tid_name[]
static void PrintTags(int ntags, const TAG tags[])
MidasHistoryInterface * MakeMidasHistorySqlDebug()
static std::vector< IndexEntry * > gHistoryIndex
static const char * midas2sqlType(int tid)
static int ReadIndex(SqlBase *sql, const char *event_name)
static const char ** sql_type
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
INT i
Definition mdump.cxx:32
static int offset
Definition mgd.cxx:1500
#define message(type, str)
#define name(x)
Definition midas_macro.h:24
MUTEX_T * tm
Definition odbedit.cxx:39
BOOL match(char *pat, char *str)
Definition odbedit.cxx:190
INT j
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
std::string event_name
std::vector< TAG > tags_cache
int timestamp
std::string table_name
std::vector< IndexEntryTag > tags
int timestamp
std::string tag_name
std::string column_name
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
static double e(void)
Definition tinyexpr.c:136