Line data Source code
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 :
16 : ////////////////////////////////////////
17 : // MIDAS includes //
18 : ////////////////////////////////////////
19 :
20 : #include "midas.h"
21 : #include "history.h"
22 : #include "mstrlcpy.h"
23 :
24 : ////////////////////////////////////////
25 : // helper stuff //
26 : ////////////////////////////////////////
27 :
28 : #define FREE(x) { if (x) free(x); (x) = NULL; }
29 :
30 : ////////////////////////////////////////
31 : // Definitions extracted from midas.c //
32 : ////////////////////////////////////////
33 :
34 : /********************************************************************/
35 : /* data type sizes */
36 : static 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 */
59 : static 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
83 : static 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 :
104 : static 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 :
124 : ////////////////////////////////////////
125 : // Handling of data types //
126 : ////////////////////////////////////////
127 :
128 : static const char **sql_type = NULL;
129 :
130 0 : static const char* midasTypeName(int tid)
131 : {
132 0 : assert(tid>=0);
133 0 : assert(tid<15);
134 0 : return tid_name[tid];
135 : }
136 :
137 0 : static const char* midas2sqlType(int tid)
138 : {
139 0 : assert(tid>=0);
140 0 : assert(tid<15);
141 0 : return sql_type[tid];
142 : }
143 :
144 0 : static int sql2midasType(const char* name)
145 : {
146 0 : for (int tid=0; tid<15; tid++)
147 0 : if (strcasecmp(name, sql_type[tid])==0)
148 0 : return tid;
149 0 : printf("sql2midasType: Cannot convert SQL data type \'%s\' to a MIDAS data type!\n", name);
150 0 : return 0;
151 : }
152 :
153 0 : static 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 0 : if (sql2midasType(sqlType) == tid)
160 0 : return true;
161 :
162 0 : if (strcasecmp(midas2sqlType(tid), sqlType) == 0)
163 0 : return true;
164 :
165 : // permit writing FLOAT into DOUBLE
166 0 : if (tid==TID_FLOAT && strcmp(sqlType, "double")==0)
167 0 : return true;
168 :
169 : // T2K quirk!
170 : // permit writing BYTE into signed tinyint
171 0 : if (tid==TID_UINT8 && strcmp(sqlType, "tinyint")==0)
172 0 : return true;
173 :
174 : // T2K quirk!
175 : // permit writing WORD into signed tinyint
176 0 : if (tid==TID_UINT16 && strcmp(sqlType, "tinyint")==0)
177 0 : return true;
178 :
179 0 : return false;
180 : }
181 :
182 : /////////////////////////////////////////////////
183 : // Base class for access to SQL functions //
184 : /////////////////////////////////////////////////
185 :
186 : class SqlBase
187 : {
188 : public:
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 0 : virtual ~SqlBase() { }; // virtual dtor
202 : };
203 :
204 : ////////////////////////////////////////////////////////////////////
205 : // SqlDebug: for debugging: write all SQL commands to stdout //
206 : ////////////////////////////////////////////////////////////////////
207 :
208 : class SqlDebug: public SqlBase
209 : {
210 : public:
211 : FILE *fp;
212 : bool fIsConnected;
213 : int fDebug;
214 :
215 : public:
216 :
217 0 : SqlDebug() // ctor
218 0 : {
219 0 : fp = NULL;
220 0 : fIsConnected = false;
221 0 : }
222 :
223 0 : ~SqlDebug() // dtor
224 0 : {
225 0 : if (fp)
226 0 : fclose(fp);
227 0 : fp = NULL;
228 0 : }
229 :
230 0 : int SetDebug(int debug)
231 : {
232 0 : int old_debug = fDebug;
233 0 : fDebug = debug;
234 0 : return old_debug;
235 : }
236 :
237 0 : int Connect(const char* filename = NULL)
238 : {
239 0 : if (!filename)
240 0 : filename = "/dev/fd/1";
241 0 : fp = fopen(filename, "w");
242 0 : assert(fp);
243 0 : sql_type = sql_type_mysql;
244 0 : fIsConnected = true;
245 0 : return DB_SUCCESS;
246 : }
247 :
248 0 : int Exec(const char* sql)
249 : {
250 0 : fprintf(fp, "%s\n", sql);
251 0 : return DB_SUCCESS;
252 : }
253 :
254 0 : int Disconnect()
255 : {
256 : // do nothing
257 0 : fIsConnected = false;
258 0 : return DB_SUCCESS;
259 : }
260 :
261 0 : bool IsConnected()
262 : {
263 0 : return fIsConnected;
264 : }
265 :
266 0 : int GetNumRows() { return DB_SUCCESS; }
267 0 : int GetNumColumns() { return DB_SUCCESS; }
268 0 : int Fetch() { return DB_NO_MORE_SUBKEYS; }
269 0 : int Done() { return DB_SUCCESS; }
270 0 : int ListTables(std::vector<std::string> *plist) { return DB_SUCCESS; };
271 0 : int ListColumns(const char* table, std::vector<std::string> *plist) { return DB_SUCCESS; };
272 0 : const char* GetColumn(int icol) { return NULL; };
273 : };
274 :
275 : #ifdef HAVE_ODBC
276 :
277 : ////////////////////////////////////////
278 : // ODBC includes //
279 : ////////////////////////////////////////
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 :
290 : //////////////////////////////////////////
291 : // SqlODBC: SQL access through ODBC //
292 : //////////////////////////////////////////
293 :
294 : class SqlODBC: public SqlBase
295 : {
296 : public:
297 : bool fIsConnected;
298 :
299 : std::string fDSN;
300 :
301 : int fDebug;
302 :
303 : SQLHENV fEnv;
304 : SQLHDBC fDB;
305 : SQLHSTMT fStmt;
306 :
307 : SqlODBC(); // ctor
308 : ~SqlODBC(); // dtor
309 :
310 0 : int SetDebug(int debug)
311 : {
312 0 : int old_debug = fDebug;
313 0 : fDebug = debug;
314 0 : 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 :
332 : protected:
333 : void ReportErrors(const char* from, const char* sqlfunc, int status);
334 : int DecodeError();
335 : };
336 :
337 0 : SqlODBC::SqlODBC() // ctor
338 : {
339 0 : fIsConnected = false;
340 0 : fDebug = 0;
341 0 : }
342 :
343 0 : SqlODBC::~SqlODBC() // dtor
344 : {
345 0 : Disconnect();
346 0 : }
347 :
348 0 : int SqlODBC::Connect(const char* dsn)
349 : {
350 0 : if (fIsConnected)
351 0 : Disconnect();
352 :
353 0 : fDSN = dsn;
354 :
355 0 : int status = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fEnv);
356 :
357 0 : if (!SQL_SUCCEEDED(status)) {
358 0 : cm_msg(MERROR, "SqlODBC::Connect", "SQLAllocHandle(SQL_HANDLE_ENV) error %d", status);
359 0 : return DB_FILE_ERROR;
360 : }
361 :
362 0 : status = SQLSetEnvAttr(fEnv,
363 : SQL_ATTR_ODBC_VERSION,
364 : (void*)SQL_OV_ODBC2,
365 : 0);
366 0 : if (!SQL_SUCCEEDED(status)) {
367 0 : cm_msg(MERROR, "SqlODBC::Connect", "SQLSetEnvAttr() error %d", status);
368 0 : SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
369 0 : return DB_FILE_ERROR;
370 : }
371 :
372 0 : status = SQLAllocHandle(SQL_HANDLE_DBC, fEnv, &fDB);
373 0 : if (!SQL_SUCCEEDED(status)) {
374 0 : cm_msg(MERROR, "SqlODBC::Connect", "SQLAllocHandle(SQL_HANDLE_DBC) error %d", status);
375 0 : SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
376 0 : return DB_FILE_ERROR;
377 : }
378 :
379 0 : SQLSetConnectAttr(fDB, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
380 :
381 : if (0) {
382 : // connect to PgSQL database
383 :
384 : sql_type = sql_type_pgsql;
385 : status = SQLConnect(fDB, (SQLCHAR*) dsn, SQL_NTS,
386 : (SQLCHAR*) "xxx", SQL_NTS,
387 : (SQLCHAR*) "", SQL_NTS);
388 : }
389 :
390 : if (1) {
391 : // connect to MySQL database
392 :
393 0 : sql_type = sql_type_mysql;
394 0 : status = SQLConnect(fDB, (SQLCHAR*) dsn, SQL_NTS,
395 : (SQLCHAR*) NULL, SQL_NTS,
396 : (SQLCHAR*) NULL, SQL_NTS);
397 : }
398 :
399 0 : if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
400 : SQLINTEGER V_OD_err;
401 : SQLSMALLINT V_OD_mlen;
402 : SQLCHAR V_OD_stat[10]; // Status SQL
403 : SQLCHAR V_OD_msg[200];
404 :
405 0 : SQLGetDiagRec(SQL_HANDLE_DBC, fDB, 1, V_OD_stat, &V_OD_err, V_OD_msg, 100, &V_OD_mlen);
406 0 : cm_msg(MERROR, "SqlODBC::Connect", "SQLConnect() error %d, %s (%d)", status, V_OD_msg,V_OD_err);
407 0 : SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
408 0 : return DB_FILE_ERROR;
409 : }
410 :
411 0 : SQLAllocHandle(SQL_HANDLE_STMT, fDB, &fStmt);
412 :
413 0 : if (fDebug)
414 0 : cm_msg(MINFO, "SqlODBC::Connect", "Connected to ODBC database DSN \'%s\'", dsn);
415 :
416 0 : fIsConnected = true;
417 :
418 0 : return DB_SUCCESS;
419 : }
420 :
421 0 : int SqlODBC::Disconnect()
422 : {
423 0 : if (!fIsConnected)
424 0 : return DB_SUCCESS;
425 :
426 0 : SQLDisconnect(fDB);
427 :
428 0 : SQLFreeHandle(SQL_HANDLE_DBC, fDB);
429 0 : SQLFreeHandle(SQL_HANDLE_STMT, fStmt);
430 0 : SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
431 :
432 0 : fIsConnected = false;
433 :
434 0 : return DB_SUCCESS;
435 : }
436 :
437 0 : bool SqlODBC::IsConnected()
438 : {
439 0 : return fIsConnected;
440 : }
441 :
442 0 : void SqlODBC::ReportErrors(const char* from, const char* sqlfunc, int status)
443 : {
444 0 : if (fDebug)
445 0 : printf("%s: %s error %d\n", from, sqlfunc, status);
446 :
447 0 : for (int i=1; ; i++) {
448 : SQLCHAR state[10]; // Status SQL
449 : SQLINTEGER error;
450 : SQLCHAR message[1024];
451 : SQLSMALLINT mlen;
452 :
453 0 : status = SQLGetDiagRec(SQL_HANDLE_STMT,
454 : fStmt,
455 : i,
456 : state,
457 : &error,
458 : message,
459 : sizeof(message),
460 : &mlen);
461 :
462 0 : if (status == SQL_NO_DATA)
463 0 : break;
464 :
465 0 : if (!SQL_SUCCEEDED(status)) {
466 0 : cm_msg(MERROR, "SqlODBC::ReportErrors", "SQLGetDiagRec() error %d", status);
467 0 : 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 0 : if (fDebug)
474 0 : printf("%s: %s warning: state: \'%s\', message: \'%s\', native error: %d\n", from, sqlfunc, state, message, (int)error);
475 0 : 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 0 : }
482 0 : }
483 :
484 0 : int SqlODBC::DecodeError()
485 : {
486 : // returns:
487 : // DB_SUCCESS
488 : // DB_NO_KEY
489 : // DB_KEY_EXIST
490 :
491 0 : for (int i=1; ; i++) {
492 : SQLCHAR state[10]; // Status SQL
493 : SQLINTEGER error;
494 : SQLCHAR message[1024];
495 : SQLSMALLINT mlen;
496 :
497 0 : error = 0;
498 :
499 0 : int status = SQLGetDiagRec(SQL_HANDLE_STMT,
500 : fStmt,
501 : i,
502 : state,
503 : &error,
504 : message,
505 : sizeof(message),
506 0 : &mlen);
507 :
508 0 : if (status == SQL_NO_DATA)
509 0 : return DB_SUCCESS;
510 :
511 0 : if (error==1146)
512 0 : return DB_NO_KEY;
513 :
514 0 : if (error==1050)
515 0 : return DB_KEY_EXIST;
516 0 : }
517 : }
518 :
519 0 : int SqlODBC::ListTables(std::vector<std::string> *plist)
520 : {
521 0 : if (!fIsConnected)
522 0 : return DB_FILE_ERROR;
523 :
524 0 : for (int i=0; i<2; i++) {
525 0 : if (fDebug)
526 0 : printf("SqlODBC::ListTables!\n");
527 :
528 : /* Retrieve a list of tables */
529 0 : int status = SQLTables(fStmt, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR*)"TABLE", SQL_NTS);
530 :
531 0 : if (SQL_SUCCEEDED(status))
532 0 : break;
533 :
534 0 : if (fDebug)
535 0 : printf("SqlODBC::ListTables: SQLTables() error %d\n", status);
536 :
537 0 : ReportErrors("SqlODBC::ListTables", "SQLTables()", status);
538 :
539 0 : status = DecodeError();
540 :
541 : //if (status == DB_NO_KEY)
542 : // return status;
543 : //
544 : //if (status == DB_KEY_EXIST)
545 : // return status;
546 :
547 0 : cm_msg(MINFO, "SqlODBC::ListTables", "Reconnecting to ODBC database DSN \'%s\'", fDSN.c_str());
548 :
549 : // try to reconnect
550 0 : std::string dsn = fDSN;
551 0 : Disconnect();
552 0 : status = Connect(dsn.c_str());
553 :
554 0 : if (!fIsConnected) {
555 0 : cm_msg(MERROR, "SqlODBC::ListTables", "Cannot reconnect to ODBC database DSN \'%s\', status %d. Database is down?", fDSN.c_str(), status);
556 0 : return DB_FILE_ERROR;
557 : }
558 :
559 0 : cm_msg(MINFO, "SqlODBC::ListTables", "Reconnected to ODBC database DSN \'%s\'", fDSN.c_str());
560 0 : }
561 :
562 0 : int ncols = GetNumColumns();
563 0 : int nrows = GetNumRows();
564 :
565 0 : if (ncols <= 0 || nrows <= 0) {
566 0 : cm_msg(MERROR, "SqlODBC::ListTables", "Error: SQLTables() returned unexpected number of columns %d or number of rows %d", ncols, nrows);
567 : }
568 :
569 0 : int row = 0;
570 : while (1) {
571 0 : int status = Fetch();
572 0 : if (status != DB_SUCCESS)
573 0 : 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 0 : plist->push_back(GetColumn(3));
586 0 : }
587 :
588 0 : Done();
589 :
590 0 : return DB_SUCCESS;
591 : }
592 :
593 0 : int SqlODBC::ListColumns(const char* table, std::vector<std::string> *plist)
594 : {
595 0 : if (!fIsConnected)
596 0 : return DB_FILE_ERROR;
597 :
598 0 : for (int i=0; i<2; i++) {
599 0 : if (fDebug)
600 0 : printf("SqlODBC::ListColumns for table \'%s\'\n", table);
601 :
602 : /* Retrieve a list of columns */
603 0 : int status = SQLColumns(fStmt, NULL, 0, NULL, 0, (SQLCHAR*)table, SQL_NTS, NULL, 0);
604 :
605 0 : if (SQL_SUCCEEDED(status))
606 0 : break;
607 :
608 0 : if (fDebug)
609 0 : printf("SqlODBC::ListColumns: SQLColumns(%s) error %d\n", table, status);
610 :
611 0 : ReportErrors("SqlODBC::ListColumns", "SQLColumns()", status);
612 :
613 0 : status = DecodeError();
614 :
615 : //if (status == DB_NO_KEY)
616 : // return status;
617 : //
618 : //if (status == DB_KEY_EXIST)
619 : // return status;
620 :
621 0 : cm_msg(MINFO, "SqlODBC::ListColumns", "Reconnecting to ODBC database DSN \'%s\'", fDSN.c_str());
622 :
623 : // try to reconnect
624 0 : std::string dsn = fDSN;
625 0 : Disconnect();
626 0 : status = Connect(dsn.c_str());
627 :
628 0 : if (!fIsConnected) {
629 0 : cm_msg(MERROR, "SqlODBC::ListColumns", "Cannot reconnect to ODBC database DSN \'%s\', status %d. Database is down?", fDSN.c_str(), status);
630 0 : return DB_FILE_ERROR;
631 : }
632 :
633 0 : cm_msg(MINFO, "SqlODBC::ListColumns", "Reconnected to ODBC database DSN \'%s\'", fDSN.c_str());
634 0 : }
635 :
636 0 : int ncols = GetNumColumns();
637 0 : int nrows = GetNumRows(); // nrows seems to be always "-1"
638 :
639 0 : if (ncols <= 0 /*|| nrows <= 0*/) {
640 0 : 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 0 : int row = 0;
646 : while (1) {
647 0 : int status = Fetch();
648 0 : if (status != DB_SUCCESS)
649 0 : 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 0 : plist->push_back(GetColumn(4)); // column name
662 0 : plist->push_back(GetColumn(6)); // column type
663 0 : }
664 :
665 0 : Done();
666 :
667 0 : return DB_SUCCESS;
668 : }
669 :
670 0 : int 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 0 : if (!fIsConnected)
678 0 : return DB_FILE_ERROR;
679 :
680 : int status;
681 :
682 0 : for (int i=0; i<2; i++) {
683 0 : if (fDebug)
684 0 : printf("SqlODBC::Exec: %s\n", sql);
685 :
686 0 : status = SQLExecDirect(fStmt,(SQLCHAR*)sql,SQL_NTS);
687 :
688 0 : if (SQL_SUCCEEDED(status)) {
689 0 : return DB_SUCCESS;
690 : }
691 :
692 0 : if (fDebug)
693 0 : printf("SqlODBC::Exec: SQLExecDirect() error %d: SQL command: \"%s\"\n", status, sql);
694 :
695 0 : ReportErrors("SqlODBC::Exec", "SQLExecDirect()", status);
696 :
697 0 : status = DecodeError();
698 :
699 0 : if (status == DB_NO_KEY)
700 0 : return status;
701 :
702 0 : if (status == DB_KEY_EXIST)
703 0 : return status;
704 :
705 0 : cm_msg(MINFO, "SqlODBC::Exec", "Reconnecting to ODBC database DSN \'%s\'", fDSN.c_str());
706 :
707 : // try to reconnect
708 0 : std::string dsn = fDSN;
709 0 : Disconnect();
710 0 : status = Connect(dsn.c_str());
711 :
712 0 : if (!fIsConnected) {
713 0 : cm_msg(MERROR, "SqlODBC::Exec", "Cannot reconnect to ODBC database DSN \'%s\', status %d. Database is down?", fDSN.c_str(), status);
714 0 : return DB_FILE_ERROR;
715 : }
716 :
717 0 : cm_msg(MINFO, "SqlODBC::Exec", "Reconnected to ODBC database DSN \'%s\'", fDSN.c_str());
718 0 : }
719 :
720 0 : return DB_SUCCESS;
721 : }
722 :
723 0 : int SqlODBC::GetNumRows()
724 : {
725 0 : SQLLEN nrows = 0;
726 : /* How many rows are there */
727 0 : int status = SQLRowCount(fStmt, &nrows);
728 0 : if (!SQL_SUCCEEDED(status)) {
729 0 : ReportErrors("SqlODBC::GetNumRow", "SQLRowCount()", status);
730 0 : return -1;
731 : }
732 0 : return nrows;
733 : }
734 :
735 0 : int SqlODBC::GetNumColumns()
736 : {
737 0 : SQLSMALLINT ncols = 0;
738 : /* How many columns are there */
739 0 : int status = SQLNumResultCols(fStmt, &ncols);
740 0 : if (!SQL_SUCCEEDED(status)) {
741 0 : ReportErrors("SqlODBC::GetNumColumns", "SQLNumResultCols()", status);
742 0 : return -1;
743 : }
744 0 : return ncols;
745 : }
746 :
747 0 : int SqlODBC::Fetch()
748 : {
749 0 : int status = SQLFetch(fStmt);
750 :
751 0 : if (status == SQL_NO_DATA)
752 0 : return DB_NO_MORE_SUBKEYS;
753 :
754 0 : if (!SQL_SUCCEEDED(status)) {
755 0 : ReportErrors("SqlODBC::Fetch", "SQLFetch()", status);
756 0 : return DB_FILE_ERROR;
757 : }
758 :
759 0 : return DB_SUCCESS;
760 : }
761 :
762 0 : int SqlODBC::Done()
763 : {
764 0 : int status = SQLCloseCursor(fStmt);
765 0 : if (!SQL_SUCCEEDED(status)) {
766 0 : ReportErrors("SqlODBC::Done", "SQLCloseCursor()", status);
767 0 : return DB_FILE_ERROR;
768 : }
769 0 : return DB_SUCCESS;
770 : }
771 :
772 0 : const char* SqlODBC::GetColumn(int icol)
773 : {
774 : static char buf[1024];
775 : SQLLEN indicator;
776 0 : int status = SQLGetData(fStmt, icol, SQL_C_CHAR, buf, sizeof(buf), &indicator);
777 :
778 0 : if (!SQL_SUCCEEDED(status)) {
779 0 : return NULL;
780 : }
781 :
782 0 : if (indicator == SQL_NULL_DATA)
783 0 : return NULL;
784 :
785 0 : return buf;
786 : }
787 :
788 : #endif
789 :
790 : //////////////////////////////////////////
791 : // Done with SQL stuff //
792 : //////////////////////////////////////////
793 :
794 : ////////////////////////////////////////////////////////
795 : // Data structures to keep track of Events and Tags //
796 : ////////////////////////////////////////////////////////
797 :
798 : struct Tag
799 : {
800 : std::string column_name;
801 : int offset;
802 : TAG tag;
803 : bool create;
804 : };
805 :
806 : struct Event
807 : {
808 : std::string event_name;
809 : std::string table_name;
810 : std::vector<Tag> tags;
811 : bool active;
812 :
813 0 : Event() // ctor
814 0 : {
815 0 : active = false;
816 0 : }
817 :
818 0 : ~Event() // dtor
819 : {
820 0 : active = false;
821 0 : }
822 : };
823 :
824 0 : static void PrintTags(int ntags, const TAG tags[])
825 : {
826 0 : for (int i=0; i<ntags; i++)
827 0 : printf("tag %d: %s %s[%d]\n", i, midasTypeName(tags[i].type), tags[i].name, tags[i].n_data);
828 0 : }
829 :
830 0 : int 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 0 : int n = e->tags.size();
835 :
836 0 : std::string tags;
837 0 : std::string values;
838 :
839 : //if (n>0)
840 : // printf(" %s", ctime(&t));
841 :
842 0 : for (int i=0; i<n; i++) {
843 0 : const Tag*t = &e->tags[i];
844 :
845 0 : if (t) {
846 0 : int offset = t->offset;
847 0 : void* ptr = (void*)(buf+offset);
848 :
849 0 : int arraySize = t->tag.n_data;
850 :
851 0 : for (int j=0; j<arraySize; j++) {
852 0 : tags += ", ";
853 0 : values += ", ";
854 :
855 0 : if (arraySize <= 1)
856 0 : tags += t->column_name;
857 : else {
858 0 : tags += t->column_name;
859 : char s[256];
860 0 : sprintf(s,"_%d", j);
861 0 : tags += s;
862 : }
863 :
864 : char s[1024];
865 :
866 0 : switch (t->tag.type) {
867 0 : default:
868 0 : sprintf(s, "unknownType%d", t->tag.type);
869 0 : break;
870 0 : case 1: /* BYTE */
871 0 : sprintf(s, "%u",((unsigned char*)ptr)[j]);
872 0 : break;
873 0 : case 2: /* SBYTE */
874 0 : sprintf(s, "%d",((signed char*)ptr)[j]);
875 0 : break;
876 0 : case 3: /* CHAR */
877 0 : sprintf(s, "\'%c\'",((char*)ptr)[j]);
878 0 : break;
879 0 : case 4: /* WORD */
880 0 : sprintf(s, "%u",((unsigned short*)ptr)[j]);
881 0 : break;
882 0 : case 5: /* SHORT */
883 0 : sprintf(s, "%d",((signed short*)ptr)[j]);
884 0 : break;
885 0 : case 6: /* DWORD */
886 0 : sprintf(s, "%u",((unsigned int*)ptr)[j]);
887 0 : break;
888 0 : case 7: /* INT */
889 0 : sprintf(s, "%d",((int*)ptr)[j]);
890 0 : break;
891 0 : case 8: /* BOOL */
892 0 : sprintf(s, "%u",((unsigned int*)ptr)[j]);
893 0 : break;
894 0 : case 9: /* FLOAT */
895 0 : sprintf(s, "\'%.8g\'",((float*)ptr)[j]);
896 0 : break;
897 0 : case 10: /* DOUBLE */
898 0 : sprintf(s, "\'%.16g\'",((double*)ptr)[j]);
899 0 : break;
900 : }
901 :
902 0 : values += s;
903 : }
904 : }
905 : }
906 :
907 : // 2001-02-16 20:38:40.1
908 : struct tm tms;
909 0 : localtime_r(&t, &tms); // somebody must call tzset() before this
910 : char s[1024];
911 0 : strftime(s,sizeof(s)-1,"%Y-%m-%d %H:%M:%S.0",&tms);
912 :
913 : char sss[102400];
914 0 : 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 0 : int status = sql->Exec(sss);
922 :
923 0 : if (status != DB_SUCCESS) {
924 0 : return status;
925 : }
926 :
927 0 : return HS_SUCCESS;
928 0 : }
929 :
930 : // convert MIDAS names to SQL names
931 :
932 0 : static std::string MidasNameToSqlName(const char* s)
933 : {
934 0 : std::string out;
935 :
936 0 : for (int i=0; s[i]!=0; i++) {
937 0 : char c = s[i];
938 0 : if (isalpha(c) || isdigit(c))
939 0 : out += tolower(c);
940 : else
941 0 : out += '_';
942 : }
943 :
944 0 : return out;
945 0 : }
946 :
947 : struct IndexEntryTag
948 : {
949 : std::string tag_name;
950 : std::string column_name;
951 : int timestamp;
952 : };
953 :
954 : struct IndexEntry
955 : {
956 : std::string event_name;
957 : std::string table_name;
958 : int timestamp;
959 : std::vector<IndexEntryTag> tags;
960 : std::vector<TAG> tags_cache;
961 : };
962 :
963 : static std::vector<IndexEntry*> gHistoryIndex;
964 :
965 : #if 0
966 : static void PrintIndex()
967 : {
968 : for (unsigned i=0; i<gHistoryIndex.size(); i++) {
969 : IndexEntry *e = gHistoryIndex[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 :
979 0 : static IndexEntry* FindIndexByTableName(const char* table_name)
980 : {
981 0 : for (unsigned i=0; i<gHistoryIndex.size(); i++)
982 0 : if (equal_ustring(gHistoryIndex[i]->table_name.c_str(), table_name)) {
983 0 : return gHistoryIndex[i];
984 : }
985 0 : return NULL;
986 : }
987 :
988 0 : static IndexEntry* FindIndexByEventName(const char* event_name)
989 : {
990 0 : for (unsigned i=0; i<gHistoryIndex.size(); i++)
991 0 : if (equal_ustring(gHistoryIndex[i]->event_name.c_str(), event_name)) {
992 0 : return gHistoryIndex[i];
993 : }
994 0 : return NULL;
995 : }
996 :
997 0 : static IndexEntryTag* FindIndexByTagName(IndexEntry* ie, const char* tag_name)
998 : {
999 0 : for (unsigned i=0; i<ie->tags.size(); i++)
1000 0 : if (equal_ustring(ie->tags[i].tag_name.c_str(), tag_name)) {
1001 0 : return &ie->tags[i];
1002 : }
1003 0 : return NULL;
1004 : }
1005 :
1006 0 : static IndexEntryTag* FindIndexByColumnName(IndexEntry* ie, const char* column_name)
1007 : {
1008 0 : for (unsigned i=0; i<ie->tags.size(); i++)
1009 0 : if (equal_ustring(ie->tags[i].column_name.c_str(), column_name)) {
1010 0 : return &ie->tags[i];
1011 : }
1012 0 : return NULL;
1013 : }
1014 :
1015 : static int gHaveIndex = true;
1016 : static int gHaveIndexAll = false;
1017 :
1018 : static int gTrace = 0;
1019 :
1020 0 : static int ReadIndex(SqlBase* sql, const char* event_name)
1021 : {
1022 0 : if (gTrace)
1023 0 : printf("ReadIndex [%s]\n", event_name);
1024 :
1025 0 : if (!gHaveIndex)
1026 0 : return HS_FILE_ERROR;
1027 :
1028 0 : if (gHaveIndexAll)
1029 0 : return HS_SUCCESS;
1030 :
1031 0 : if (gTrace)
1032 0 : printf("ReadIndex: reading index for event [%s]\n", event_name);
1033 :
1034 : //event_name = NULL;
1035 :
1036 : char cmd[256];
1037 :
1038 0 : if (event_name)
1039 0 : sprintf(cmd, "SELECT event_name, table_name, tag_name, column_name, itimestamp FROM _history_index where event_name=\'%s\';", event_name);
1040 : else
1041 0 : sprintf(cmd, "SELECT event_name, table_name, tag_name, column_name, itimestamp FROM _history_index;");
1042 :
1043 0 : int status = sql->Exec(cmd);
1044 :
1045 0 : if (status == DB_NO_KEY) {
1046 0 : gHaveIndex = false;
1047 0 : return HS_FILE_ERROR;
1048 : }
1049 :
1050 0 : if (gTrace) {
1051 0 : printf("ReadIndex: event %s, Read status %d, nrows: %d\n",
1052 : event_name,
1053 : status,
1054 0 : sql->GetNumRows());
1055 : }
1056 :
1057 0 : if (status != SUCCESS)
1058 0 : return HS_FILE_ERROR;
1059 :
1060 0 : if (sql->GetNumRows() == 0) {
1061 0 : sql->Done();
1062 0 : return HS_FILE_ERROR;
1063 : }
1064 :
1065 0 : int nrows = sql->GetNumRows();
1066 0 : int ncols = sql->GetNumColumns();
1067 :
1068 0 : if (nrows == 0)
1069 0 : return HS_SUCCESS;
1070 :
1071 0 : if (gTrace)
1072 0 : printf("ReadIndex: event %s, nrows: %d, ncols: %d\n",
1073 : event_name,
1074 : nrows, ncols);
1075 :
1076 0 : if (nrows < 0)
1077 0 : return HS_FILE_ERROR;
1078 :
1079 0 : if (ncols < 1)
1080 0 : return HS_FILE_ERROR;
1081 :
1082 : /* Loop through the rows in the result-set */
1083 : while (1) {
1084 0 : status = sql->Fetch();
1085 0 : if (status != DB_SUCCESS)
1086 0 : break;
1087 :
1088 0 : std::string xevent_name = sql->GetColumn(1);
1089 :
1090 0 : const char* p = sql->GetColumn(2);
1091 0 : if (p) { // table declaration
1092 0 : std::string xtable_name = p;
1093 0 : std::string xtimestamp = sql->GetColumn(5);
1094 0 : int timestamp = atoi(xtimestamp.c_str());
1095 :
1096 0 : IndexEntry* ie = FindIndexByEventName(xevent_name.c_str());
1097 0 : if (!ie) {
1098 0 : ie = new IndexEntry;
1099 0 : gHistoryIndex.push_back(ie);
1100 0 : ie->timestamp = timestamp - 1; // make sure we update this entry
1101 : }
1102 :
1103 0 : if (timestamp > ie->timestamp) {
1104 0 : ie->event_name = xevent_name;
1105 0 : ie->table_name = xtable_name;
1106 0 : 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 0 : continue;
1111 0 : }
1112 :
1113 0 : p = sql->GetColumn(3);
1114 0 : if (p) { // tag declaration
1115 0 : std::string xtag_name = p;
1116 0 : std::string xcolumn_name = sql->GetColumn(4);
1117 0 : std::string xtimestamp = sql->GetColumn(5);
1118 0 : int timestamp = atoi(xtimestamp.c_str());
1119 :
1120 0 : IndexEntry* ie = FindIndexByEventName(xevent_name.c_str());
1121 0 : if (!ie) {
1122 0 : ie = new IndexEntry;
1123 0 : gHistoryIndex.push_back(ie);
1124 0 : ie->timestamp = 0;
1125 0 : ie->event_name = xevent_name;
1126 : }
1127 :
1128 0 : bool found = false;
1129 0 : for (unsigned j=0; j<ie->tags.size(); j++)
1130 0 : if (ie->tags[j].tag_name == xtag_name) {
1131 0 : if (timestamp > ie->tags[j].timestamp) {
1132 0 : ie->tags[j].timestamp = timestamp;
1133 0 : ie->tags[j].column_name = xcolumn_name;
1134 : }
1135 0 : found = true;
1136 0 : break;
1137 : }
1138 :
1139 0 : if (!found) {
1140 0 : IndexEntryTag it;
1141 0 : it.tag_name = xtag_name;
1142 0 : it.column_name = xcolumn_name;
1143 0 : it.timestamp = timestamp;
1144 0 : ie->tags.push_back(it);
1145 0 : }
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 0 : continue;
1149 0 : }
1150 :
1151 0 : }
1152 :
1153 0 : sql->Done();
1154 :
1155 0 : gHaveIndex = true;
1156 :
1157 0 : if (event_name == NULL)
1158 0 : gHaveIndexAll = true;
1159 :
1160 : //PrintIndex();
1161 :
1162 0 : return HS_SUCCESS;
1163 : }
1164 :
1165 : ////////////////////////////////////////////////////////
1166 : // Implementation of the MidasHistoryInterface //
1167 : ////////////////////////////////////////////////////////
1168 :
1169 : class SqlHistory: public MidasHistoryInterface
1170 : {
1171 : public:
1172 : SqlBase *fSql;
1173 : int fDebug;
1174 : std::string fConnectString;
1175 : int fConnectRetry;
1176 : int fNextConnect;
1177 : std::vector<Event*> fEvents;
1178 : std::vector<std::string> fIndexEvents;
1179 : bool fHaveIndex;
1180 : bool fHaveXIndex;
1181 :
1182 0 : SqlHistory(SqlBase* b)
1183 0 : {
1184 0 : fDebug = 0;
1185 0 : fConnectRetry = 0;
1186 0 : fNextConnect = 0;
1187 0 : fSql = b;
1188 0 : fHaveIndex = false;
1189 0 : fHaveXIndex = false;
1190 0 : }
1191 :
1192 0 : ~SqlHistory()
1193 0 : {
1194 0 : hs_disconnect();
1195 0 : delete fSql;
1196 0 : fSql = NULL;
1197 0 : }
1198 :
1199 0 : int hs_set_debug(int debug)
1200 : {
1201 0 : int old = fDebug;
1202 0 : fDebug = debug;
1203 0 : gTrace = debug;
1204 0 : fSql->SetDebug(debug);
1205 0 : return old;
1206 : }
1207 :
1208 0 : int hs_connect(const char* connect_string)
1209 : {
1210 0 : if (fDebug)
1211 0 : printf("hs_connect %s!\n", connect_string);
1212 :
1213 0 : assert(fSql);
1214 :
1215 0 : if (fSql->IsConnected())
1216 0 : if (strcmp(fConnectString.c_str(), connect_string) == 0)
1217 0 : return HS_SUCCESS;
1218 :
1219 0 : hs_disconnect();
1220 :
1221 0 : fConnectString = connect_string;
1222 :
1223 0 : if (fDebug)
1224 0 : printf("hs_connect: connecting to SQL database \'%s\'\n", fConnectString.c_str());
1225 :
1226 0 : int status = fSql->Connect(fConnectString.c_str());
1227 0 : if (status != DB_SUCCESS)
1228 0 : return status;
1229 :
1230 0 : std::vector<std::string> tables;
1231 :
1232 0 : status = fSql->ListTables(&tables);
1233 0 : if (status != DB_SUCCESS)
1234 0 : return status;
1235 :
1236 0 : for (unsigned i=0; i<tables.size(); i++) {
1237 0 : if (tables[i] == "_history_index") {
1238 0 : fHaveIndex = true;
1239 0 : break;
1240 : }
1241 : }
1242 :
1243 0 : return HS_SUCCESS;
1244 0 : }
1245 :
1246 0 : int hs_disconnect()
1247 : {
1248 0 : if (fDebug)
1249 0 : printf("hs_disconnect!\n");
1250 :
1251 0 : fSql->Disconnect();
1252 :
1253 0 : hs_clear_cache();
1254 :
1255 0 : return HS_SUCCESS;
1256 : }
1257 :
1258 0 : int Reconnect()
1259 : {
1260 0 : if (fDebug)
1261 0 : printf("Reconnect to SQL database!\n");
1262 :
1263 0 : fSql->Disconnect();
1264 0 : fSql->Connect(fConnectString.c_str());
1265 0 : if (!fSql->IsConnected()) {
1266 0 : return HS_FILE_ERROR;
1267 : }
1268 :
1269 0 : return HS_SUCCESS;
1270 : }
1271 :
1272 : ////////////////////////////////////////////////////////
1273 : // Internal data caches //
1274 : ////////////////////////////////////////////////////////
1275 :
1276 0 : int hs_clear_cache()
1277 : {
1278 0 : if (fDebug)
1279 0 : printf("hs_clear_cache!\n");
1280 :
1281 0 : gHaveIndex = true;
1282 0 : gHaveIndexAll = false;
1283 0 : fHaveXIndex = false;
1284 :
1285 0 : for (unsigned i=0; i<gHistoryIndex.size(); i++) {
1286 0 : IndexEntry* ie = gHistoryIndex[i];
1287 0 : delete ie;
1288 : }
1289 0 : gHistoryIndex.clear();
1290 :
1291 0 : fIndexEvents.clear();
1292 :
1293 0 : return HS_SUCCESS;
1294 : }
1295 :
1296 0 : int XReadIndex()
1297 : {
1298 0 : if (fHaveXIndex)
1299 0 : return HS_SUCCESS;
1300 :
1301 0 : if (fDebug)
1302 0 : printf("XReadIndex!\n");
1303 :
1304 0 : std::vector<std::string> tables;
1305 :
1306 0 : int status = fSql->ListTables(&tables);
1307 0 : if (status != DB_SUCCESS)
1308 0 : return status;
1309 :
1310 0 : for (unsigned i=0; i<tables.size(); i++) {
1311 0 : if (tables[i] == "_history_index")
1312 0 : continue;
1313 :
1314 0 : IndexEntry* ie = NULL; //FindEventName(tables[i].c_str());
1315 :
1316 0 : if (!ie) {
1317 0 : ie = new IndexEntry;
1318 :
1319 0 : ie->table_name = tables[i];
1320 0 : ie->event_name = ie->table_name;
1321 :
1322 0 : gHistoryIndex.push_back(ie);
1323 : }
1324 :
1325 0 : std::vector<std::string> columns;
1326 :
1327 0 : status = fSql->ListColumns(ie->table_name.c_str(), &columns);
1328 0 : if (status != DB_SUCCESS)
1329 0 : return status;
1330 :
1331 0 : for (unsigned int j=0; j<columns.size(); j+=2) {
1332 0 : if (columns[j] == "_t_time")
1333 0 : continue;
1334 0 : if (columns[j] == "_i_time")
1335 0 : continue;
1336 :
1337 0 : IndexEntryTag t;
1338 0 : t.column_name = columns[j];
1339 0 : t.tag_name = t.column_name;
1340 0 : t.timestamp = 0;
1341 :
1342 0 : ie->tags.push_back(t);
1343 0 : }
1344 0 : }
1345 :
1346 0 : fHaveXIndex = true;
1347 :
1348 : //PrintIndex();
1349 :
1350 0 : return HS_SUCCESS;
1351 0 : }
1352 :
1353 : ////////////////////////////////////////////////////////
1354 : // Functions used by mlogger //
1355 : ////////////////////////////////////////////////////////
1356 :
1357 0 : int hs_define_event(const char* event_name, time_t timestamp, int ntags, const TAG tags[])
1358 : {
1359 : int status;
1360 :
1361 0 : if (fDebug) {
1362 0 : printf("define event [%s] with %d tags:\n", event_name, ntags);
1363 0 : PrintTags(ntags, tags);
1364 : }
1365 :
1366 : // delete all events with the same name
1367 0 : for (unsigned int i=0; i<fEvents.size(); i++)
1368 0 : if (fEvents[i])
1369 0 : if (fEvents[i]->event_name == event_name) {
1370 0 : if (fDebug)
1371 0 : printf("deleting exising event %s\n", event_name);
1372 0 : delete fEvents[i];
1373 0 : fEvents[i] = NULL;
1374 : }
1375 :
1376 0 : Event* e = new Event();
1377 :
1378 0 : e->event_name = event_name;
1379 :
1380 0 : if (!fHaveIndex) {
1381 : char buf[1024];
1382 0 : 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 0 : int status = fSql->Exec(buf);
1384 0 : if (status == DB_KEY_EXIST)
1385 : /* do nothing */ ;
1386 0 : else if (status != DB_SUCCESS)
1387 0 : return status;
1388 0 : fHaveIndex = true;
1389 : }
1390 :
1391 0 : IndexEntry* ie = FindIndexByEventName(event_name);
1392 :
1393 0 : if (!ie) {
1394 0 : ReadIndex(fSql, event_name);
1395 0 : ie = FindIndexByEventName(event_name);
1396 : }
1397 :
1398 0 : if (!ie) {
1399 0 : std::string table_name = MidasNameToSqlName(event_name);
1400 :
1401 : char sss[102400];
1402 0 : 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 0 : int status = fSql->Exec(sss);
1408 0 : if (status != DB_SUCCESS)
1409 0 : return HS_FILE_ERROR;
1410 :
1411 0 : ReadIndex(fSql, event_name);
1412 0 : ie = FindIndexByEventName(event_name);
1413 0 : }
1414 :
1415 0 : if (!ie) {
1416 0 : cm_msg(MERROR, "hs_define_event", "could not add event name to SQL history index table, see messages");
1417 0 : return HS_FILE_ERROR;
1418 : }
1419 :
1420 0 : e->table_name = ie->table_name;
1421 0 : e->active = true;
1422 :
1423 0 : bool create_event = false;
1424 :
1425 0 : int offset = 0;
1426 0 : for (int i=0; i<ntags; i++) {
1427 0 : for (unsigned int j=0; j<tags[i].n_data; j++) {
1428 0 : std::string tagname = tags[i].name;
1429 0 : std::string colname = MidasNameToSqlName(tags[i].name);
1430 :
1431 0 : if (tags[i].n_data > 1) {
1432 : char s[256];
1433 0 : sprintf(s, "[%d]", j);
1434 0 : tagname += s;
1435 :
1436 0 : sprintf(s, "_%d", j);
1437 0 : colname += s;
1438 : }
1439 :
1440 0 : IndexEntryTag *it = FindIndexByTagName(ie, tagname.c_str());
1441 0 : if (!it) {
1442 : // check for duplicate names
1443 :
1444 : while (1) {
1445 0 : bool dupe = false;
1446 :
1447 0 : for (unsigned i=0; i<e->tags.size(); i++) {
1448 0 : if (colname == e->tags[i].column_name) {
1449 : //printf("duplicate name %s\n", colname.c_str());
1450 0 : dupe = true;
1451 0 : break;
1452 : }
1453 : }
1454 :
1455 0 : if (!dupe)
1456 0 : break;
1457 :
1458 : char s[256];
1459 0 : sprintf(s, "_%d", rand());
1460 0 : colname += s;
1461 0 : }
1462 :
1463 : // add tag name to column name translation to the history index
1464 :
1465 : char sss[102400];
1466 0 : 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 0 : int status = fSql->Exec(sss);
1473 0 : if (status != DB_SUCCESS)
1474 0 : return HS_FILE_ERROR;
1475 :
1476 : // reload the history index
1477 :
1478 0 : ReadIndex(fSql, event_name);
1479 0 : ie = FindIndexByEventName(event_name);
1480 0 : assert(ie);
1481 0 : it = FindIndexByTagName(ie, tagname.c_str());
1482 : }
1483 :
1484 0 : if (!it) {
1485 0 : cm_msg(MERROR, "hs_define_event", "could not add event tags to SQL history index table, see messages");
1486 0 : return HS_FILE_ERROR;
1487 : }
1488 :
1489 0 : Tag t;
1490 0 : t.column_name = it->column_name;
1491 0 : t.create = false;
1492 0 : t.offset = offset;
1493 0 : t.tag = tags[i];
1494 0 : t.tag.n_data = 1;
1495 0 : e->tags.push_back(t);
1496 0 : int size = tid_size[tags[i].type];
1497 0 : offset += size;
1498 0 : }
1499 : }
1500 :
1501 0 : std::vector<std::string> columns;
1502 :
1503 0 : status = fSql->ListColumns(e->table_name.c_str(), &columns);
1504 0 : if (status != DB_SUCCESS)
1505 0 : return status;
1506 :
1507 0 : if (columns.size() <= 0)
1508 0 : create_event = true;
1509 :
1510 0 : for (unsigned i=0; i<e->tags.size(); i++) {
1511 : // check for duplicate column names
1512 0 : for (unsigned j=i+1; j<e->tags.size(); j++)
1513 0 : if (e->tags[i].column_name == e->tags[j].column_name) {
1514 0 : 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 0 : e->active = false;
1516 0 : break;
1517 : }
1518 :
1519 : // check if new column needs to be created
1520 0 : bool found = false;
1521 0 : for (size_t j=0; j<columns.size(); j+=2) {
1522 0 : 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 0 : if (!isCompatible(e->tags[i].tag.type, columns[j+1].c_str())) {
1527 0 : 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 0 : e->active = false;
1529 : }
1530 :
1531 0 : found = true;
1532 0 : break;
1533 : }
1534 : }
1535 :
1536 0 : 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 0 : e->tags[i].create = true;
1540 : }
1541 : }
1542 :
1543 0 : if (create_event) {
1544 : char buf[1024];
1545 0 : 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 0 : status = fSql->Exec(buf);
1547 0 : if (status != DB_SUCCESS) {
1548 0 : e->active = false;
1549 0 : return HS_FILE_ERROR;
1550 : }
1551 : }
1552 :
1553 0 : for (size_t i=0; i<e->tags.size(); i++)
1554 0 : if (e->tags[i].create) {
1555 : char buf[1024];
1556 :
1557 0 : sprintf(buf, "ALTER TABLE %s ADD COLUMN %s %s;",
1558 0 : e->table_name.c_str(),
1559 0 : e->tags[i].column_name.c_str(),
1560 0 : midas2sqlType(e->tags[i].tag.type));
1561 :
1562 0 : status = fSql->Exec(buf);
1563 :
1564 0 : if (status != DB_SUCCESS) {
1565 0 : e->active = false;
1566 0 : return HS_FILE_ERROR;
1567 : }
1568 : }
1569 :
1570 : // find empty slot in events list
1571 0 : for (unsigned int i=0; i<fEvents.size(); i++)
1572 0 : if (!fEvents[i]) {
1573 0 : fEvents[i] = e;
1574 0 : e = NULL;
1575 0 : break;
1576 : }
1577 :
1578 : // if no empty slots, add at the end
1579 0 : if (e)
1580 0 : fEvents.push_back(e);
1581 :
1582 0 : return HS_SUCCESS;
1583 0 : }
1584 :
1585 0 : int hs_write_event(const char* event_name, time_t timestamp, int buffer_size, const char* buffer)
1586 : {
1587 0 : if (fDebug)
1588 0 : 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 0 : if (!fSql->IsConnected()) {
1593 0 : time_t now = time(NULL);
1594 :
1595 : // too early to try reconnecting?
1596 0 : if (fConnectRetry !=0 && now < fNextConnect) {
1597 0 : return HS_FILE_ERROR;
1598 : }
1599 :
1600 0 : cm_msg(MINFO, "hs_write_event", "Trying to reconnect to SQL database \'%s\'", fConnectString.c_str());
1601 :
1602 0 : int status = fSql->Connect(fConnectString.c_str());
1603 :
1604 0 : if (status != DB_SUCCESS) {
1605 :
1606 : // first retry in 5 seconds
1607 0 : if (fConnectRetry == 0)
1608 0 : fConnectRetry = 5;
1609 :
1610 0 : fNextConnect = (int)(now + fConnectRetry);
1611 :
1612 : // exponential backoff
1613 0 : fConnectRetry *= 2;
1614 :
1615 : // but no more than every 10 minutes
1616 0 : if (fConnectRetry > 10*60)
1617 0 : fConnectRetry = 10*60;
1618 :
1619 0 : return HS_FILE_ERROR;
1620 : }
1621 :
1622 0 : cm_msg(MINFO, "hs_write_event", "Reconnected to SQL database \'%s\'", fConnectString.c_str());
1623 : }
1624 :
1625 0 : fNextConnect = 0;
1626 0 : fConnectRetry = 0;
1627 :
1628 0 : Event *e = NULL;
1629 :
1630 : // find this event
1631 0 : for (size_t i=0; i<fEvents.size(); i++)
1632 0 : if (fEvents[i]->event_name == event_name) {
1633 0 : e = fEvents[i];
1634 0 : break;
1635 : }
1636 :
1637 : // not found
1638 0 : if (!e)
1639 0 : return HS_UNDEFINED_EVENT;
1640 :
1641 : // deactivated because of error?
1642 0 : if (!e->active)
1643 0 : return HS_FILE_ERROR;
1644 :
1645 0 : int status = WriteEvent(fSql, e, timestamp, buffer, buffer_size);
1646 :
1647 : // if could not write to SQL?
1648 0 : if (status != HS_SUCCESS) {
1649 :
1650 : // if lost SQL connection, try again later
1651 :
1652 0 : if (!fSql->IsConnected()) {
1653 0 : return HS_FILE_ERROR;
1654 : }
1655 :
1656 : // otherwise, deactivate this event
1657 :
1658 0 : e->active = false;
1659 :
1660 0 : 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 0 : return HS_FILE_ERROR;
1663 : }
1664 :
1665 0 : return HS_SUCCESS;
1666 : }
1667 :
1668 0 : int hs_flush_buffers()
1669 : {
1670 0 : if (fDebug)
1671 0 : printf("hs_flush_buffers!\n");
1672 0 : return HS_SUCCESS;
1673 : }
1674 :
1675 : ////////////////////////////////////////////////////////
1676 : // Functions used by mhttpd //
1677 : ////////////////////////////////////////////////////////
1678 :
1679 0 : int hs_get_events(time_t t, std::vector<std::string> *pevents)
1680 : {
1681 0 : if (fDebug)
1682 0 : printf("hs_get_events!\n");
1683 :
1684 0 : if (fIndexEvents.size() == 0) {
1685 :
1686 0 : if (fDebug)
1687 0 : printf("hs_get_events: reading event names!\n");
1688 :
1689 0 : ReadIndex(fSql, NULL);
1690 :
1691 0 : std::vector<std::string> tables;
1692 0 : int status = fSql->ListTables(&tables);
1693 0 : if (status != DB_SUCCESS)
1694 0 : return status;
1695 :
1696 0 : for (unsigned i=0; i<tables.size(); i++) {
1697 0 : if (tables[i] == "_history_index")
1698 0 : continue;
1699 :
1700 0 : IndexEntry* ie = FindIndexByTableName(tables[i].c_str());
1701 0 : if (!ie) {
1702 0 : ReadIndex(fSql, NULL);
1703 0 : ie = FindIndexByTableName(tables[i].c_str());
1704 : }
1705 :
1706 0 : if (ie)
1707 0 : fIndexEvents.push_back(ie->event_name);
1708 : else
1709 0 : fIndexEvents.push_back(tables[i]);
1710 : }
1711 0 : }
1712 :
1713 0 : assert(pevents);
1714 0 : *pevents = fIndexEvents;
1715 :
1716 0 : return HS_SUCCESS;
1717 : }
1718 :
1719 0 : int hs_get_tags(const char* event_name, time_t t, std::vector<TAG> *ptags)
1720 : {
1721 0 : if (fDebug)
1722 0 : printf("hs_get_tags for [%s]\n", event_name);
1723 :
1724 0 : assert(ptags);
1725 :
1726 0 : IndexEntry* ie = FindIndexByEventName(event_name);
1727 :
1728 0 : if (!ie) {
1729 0 : ReadIndex(fSql, event_name);
1730 0 : ie = FindIndexByEventName(event_name);
1731 : }
1732 :
1733 0 : if (!ie) {
1734 0 : XReadIndex();
1735 0 : ie = FindIndexByEventName(event_name);
1736 : }
1737 :
1738 0 : if (!ie)
1739 0 : return HS_UNDEFINED_EVENT;
1740 :
1741 0 : if (ie->tags_cache.size() == 0) {
1742 0 : if (fDebug)
1743 0 : printf("hs_get_tags reading tags for [%s]\n", event_name);
1744 :
1745 0 : std::string tname = ie->table_name;
1746 :
1747 0 : std::vector<std::string> columns;
1748 :
1749 0 : int status = fSql->ListColumns(tname.c_str(), &columns);
1750 0 : if (status != DB_SUCCESS)
1751 0 : return status;
1752 :
1753 0 : if (columns.size() < 1) {
1754 0 : cm_msg(MERROR, "hs_get_tags", "Cannot get columns for table \'%s\', try to reconnect to the database", tname.c_str());
1755 :
1756 0 : int status = Reconnect();
1757 0 : if (status != HS_SUCCESS)
1758 0 : return status;
1759 :
1760 0 : columns.clear();
1761 0 : status = fSql->ListColumns(tname.c_str(), &columns);
1762 0 : if (status != DB_SUCCESS)
1763 0 : return status;
1764 : }
1765 :
1766 : //TAG* t = (TAG*)malloc(sizeof(TAG)*columns.size());
1767 : //assert(t);
1768 :
1769 0 : for (unsigned int j=0; j<columns.size(); j+=2) {
1770 0 : if (columns[j] == "_t_time")
1771 0 : continue;
1772 0 : if (columns[j] == "_i_time")
1773 0 : continue;
1774 :
1775 0 : IndexEntryTag* it = FindIndexByColumnName(ie, columns[j].c_str());
1776 :
1777 : TAG t;
1778 0 : if (it)
1779 0 : mstrlcpy(t.name, it->tag_name.c_str(), sizeof(t.name));
1780 : else
1781 0 : mstrlcpy(t.name, columns[j].c_str(), sizeof(t.name));
1782 0 : t.type = sql2midasType(columns[j+1].c_str());
1783 0 : t.n_data = 1;
1784 :
1785 0 : ie->tags_cache.push_back(t);
1786 : }
1787 0 : }
1788 :
1789 0 : for (unsigned i=0; i<ie->tags_cache.size(); i++)
1790 0 : ptags->push_back(ie->tags_cache[i]);
1791 :
1792 0 : return HS_SUCCESS;
1793 : }
1794 :
1795 0 : 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 0 : for (int i=0; i<num_var; i++)
1798 0 : last_written[i] = 0;
1799 0 : return HS_FILE_ERROR;
1800 : }
1801 :
1802 0 : 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 0 : if (fDebug) {
1808 0 : printf("hs_read_old_style: event \"%s\", tag \"%s\"\n", event_name, tag_name);
1809 : }
1810 :
1811 0 : ReadIndex(fSql, NULL);
1812 :
1813 0 : for (unsigned e=0; e<gHistoryIndex.size(); e++) {
1814 :
1815 0 : const char* s = gHistoryIndex[e]->event_name.c_str();
1816 :
1817 0 : bool match = false;
1818 0 : for (int j=0; s[j]; j++) {
1819 :
1820 0 : if ((event_name[j]==0) && (s[j]=='/')) {
1821 0 : match = true;
1822 0 : break;
1823 : }
1824 :
1825 0 : if ((event_name[j]==0) && (s[j]=='_')) {
1826 0 : match = true;
1827 0 : break;
1828 : }
1829 :
1830 0 : if (event_name[j]==0) {
1831 0 : match = false;
1832 0 : break;
1833 : }
1834 :
1835 0 : if (tolower(event_name[j]) != tolower(s[j])) {
1836 0 : match = false;
1837 0 : break;
1838 : }
1839 : }
1840 :
1841 : //printf("try %s, match %d\n", s, match);
1842 :
1843 0 : if (match) {
1844 0 : bool found_tag = false;
1845 0 : IndexEntry *ie = gHistoryIndex[e];
1846 0 : 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 0 : if (equal_ustring(tag_name, ie->tags[v].tag_name.c_str())) {
1849 0 : found_tag = true;
1850 0 : break;
1851 : }
1852 : }
1853 :
1854 0 : if (!found_tag)
1855 0 : match = false;
1856 : }
1857 :
1858 0 : if (match) {
1859 0 : if (fDebug)
1860 0 : printf("hs_read_old_style: event \"%s\", tag \"%s\", try matching event \'%s\'\n", event_name, tag_name, s);
1861 :
1862 0 : int status = hs_read(start_time, end_time, interval,
1863 : s, tag_name, var_index,
1864 : num_entries,
1865 : time_buffer, data_buffer);
1866 :
1867 0 : if (status==HS_SUCCESS && *num_entries>0)
1868 0 : return HS_SUCCESS;
1869 : }
1870 : }
1871 :
1872 0 : return HS_UNDEFINED_VAR;
1873 : }
1874 :
1875 0 : 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 0 : *num_entries = 0;
1881 0 : *time_buffer = NULL;
1882 0 : *data_buffer = NULL;
1883 :
1884 0 : if (fDebug)
1885 0 : 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 0 : start_time, end_time, end_time-start_time, interval, (end_time-start_time)/interval);
1888 :
1889 0 : if (event_name==NULL)
1890 0 : return HS_SUCCESS;
1891 :
1892 0 : IndexEntry*ie = FindIndexByEventName(event_name);
1893 :
1894 0 : if (!ie) {
1895 0 : ReadIndex(fSql, event_name);
1896 0 : ie = FindIndexByEventName(event_name);
1897 : }
1898 :
1899 0 : if (!ie) {
1900 0 : XReadIndex();
1901 0 : ie = FindIndexByEventName(event_name);
1902 : }
1903 :
1904 0 : IndexEntryTag *it = NULL;
1905 :
1906 0 : if (ie)
1907 0 : it = FindIndexByTagName(ie, tag_name);
1908 :
1909 0 : if (ie && !it) { // maybe this is an array without "Names"?
1910 : char xxx[256];
1911 0 : sprintf(xxx, "%s[%d]", tag_name, tag_index);
1912 0 : it = FindIndexByTagName(ie, xxx);
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 0 : bool oldStyleEventName = (strchr(event_name, '/')==NULL);
1918 :
1919 0 : if (oldStyleEventName)
1920 0 : if (!ie || !it) {
1921 0 : return hs_read_old_style(start_time, end_time, interval,
1922 : event_name, tag_name, tag_index,
1923 : num_entries,
1924 0 : time_buffer, data_buffer);
1925 : }
1926 :
1927 0 : if (!it)
1928 0 : return HS_UNDEFINED_VAR;
1929 :
1930 0 : assert(ie);
1931 0 : assert(it);
1932 :
1933 0 : std::string tname = ie->table_name;
1934 0 : std::string cname = it->column_name;
1935 :
1936 : char cmd[256];
1937 0 : 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 0 : int status = fSql->Exec(cmd);
1942 :
1943 0 : if (fDebug) {
1944 0 : 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 0 : fSql->GetNumRows(),
1950 0 : fSql->GetNumColumns()
1951 : );
1952 : }
1953 :
1954 0 : if (status != SUCCESS) {
1955 0 : return HS_FILE_ERROR;
1956 : }
1957 :
1958 0 : if (fSql->GetNumRows() == 0) {
1959 0 : fSql->Done();
1960 :
1961 0 : if (oldStyleEventName) {
1962 0 : return hs_read_old_style(start_time, end_time, interval,
1963 : event_name, tag_name, tag_index,
1964 : num_entries,
1965 0 : time_buffer, data_buffer);
1966 : }
1967 :
1968 0 : return HS_SUCCESS;
1969 : }
1970 :
1971 0 : int nrows = fSql->GetNumRows();
1972 0 : int ncols = fSql->GetNumColumns();
1973 :
1974 0 : if (nrows < 0)
1975 0 : return HS_FILE_ERROR;
1976 :
1977 0 : if (ncols < 1)
1978 0 : return HS_FILE_ERROR;
1979 :
1980 0 : *num_entries = 0;
1981 0 : *time_buffer = (time_t*)malloc(nrows * sizeof(time_t));
1982 0 : *data_buffer = (double*)malloc(nrows * sizeof(double));
1983 :
1984 : /* Loop through the rows in the result-set */
1985 0 : int row = 0;
1986 0 : time_t tt = 0;
1987 0 : int ann = 0;
1988 0 : double att = 0;
1989 0 : double avv = 0;
1990 : while (1) {
1991 0 : status = fSql->Fetch();
1992 0 : if (status != DB_SUCCESS)
1993 0 : break;
1994 :
1995 0 : time_t t = 0;
1996 0 : double v = 0;
1997 :
1998 0 : const char* timedata = fSql->GetColumn(1);
1999 0 : if (timedata)
2000 0 : t = atoi(timedata);
2001 :
2002 0 : const char* valuedata = fSql->GetColumn(2);
2003 0 : if (valuedata)
2004 0 : v = atof(valuedata);
2005 :
2006 0 : if (t < start_time || t > end_time)
2007 0 : 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 0 : if (tt == 0 || t >= tt + interval) {
2013 :
2014 0 : if (ann > 0) {
2015 0 : assert(row < nrows);
2016 :
2017 0 : (*time_buffer)[row] = (time_t)(att/ann);
2018 0 : (*data_buffer)[row] = avv/ann;
2019 :
2020 0 : row++;
2021 0 : (*num_entries) = row;
2022 : }
2023 :
2024 0 : ann = 0;
2025 0 : att = 0;
2026 0 : avv = 0;
2027 0 : tt = t;
2028 :
2029 : }
2030 :
2031 0 : ann++;
2032 0 : att += t;
2033 0 : avv += v;
2034 0 : }
2035 :
2036 0 : if (ann > 0) {
2037 0 : assert(row < nrows);
2038 :
2039 0 : (*time_buffer)[row] = (time_t)(att/ann);
2040 0 : (*data_buffer)[row] = avv/ann;
2041 :
2042 0 : row++;
2043 0 : (*num_entries) = row;
2044 : }
2045 :
2046 0 : fSql->Done();
2047 :
2048 0 : if (fDebug)
2049 0 : printf("hs_read: return %d entries\n", *num_entries);
2050 :
2051 0 : return HS_SUCCESS;
2052 0 : }
2053 :
2054 0 : 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 0 : if (fDebug)
2062 0 : printf("hs_read: %d variables\n", num_var);
2063 :
2064 0 : if (!fSql->IsConnected())
2065 0 : return HS_FILE_ERROR;
2066 :
2067 0 : for (int i=0; i<num_var; i++) {
2068 :
2069 0 : if (event_name[i]==NULL) {
2070 0 : st[i] = HS_UNDEFINED_EVENT;
2071 0 : num_entries[i] = 0;
2072 0 : continue;
2073 : }
2074 :
2075 0 : st[i] = hs_read((double)start_time, (double)end_time, (double)interval,
2076 0 : event_name[i], tag_name[i], tag_index[i],
2077 0 : &num_entries[i],
2078 0 : &time_buffer[i], &data_buffer[i]);
2079 : }
2080 :
2081 0 : 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[],
2090 : time_t* time_buffer[],
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 0 : 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[],
2119 : MidasHistoryBufferInterface* buffer[],
2120 : int status[])
2121 : {
2122 0 : return HS_FILE_ERROR;
2123 : }
2124 :
2125 : /*------------------------------------------------------------------*/
2126 :
2127 0 : 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[],
2131 : time_t* bins_first_time[], double* bins_first_value[],
2132 : time_t* bins_last_time[], double* bins_last_value[],
2133 : time_t last_time[], double last_value[],
2134 : int status[])
2135 : {
2136 0 : return HS_FILE_ERROR;
2137 : }
2138 : };
2139 :
2140 : ////////////////////////////////////////////////////////
2141 : // Factory constructors //
2142 : ////////////////////////////////////////////////////////
2143 :
2144 : #ifdef HAVE_ODBC
2145 0 : MidasHistoryInterface* MakeMidasHistoryODBC()
2146 : {
2147 0 : return new SqlHistory(new SqlODBC());
2148 : }
2149 : #else
2150 : MidasHistoryInterface* MakeMidasHistoryODBC()
2151 : {
2152 : return NULL;
2153 : }
2154 : #endif
2155 :
2156 0 : MidasHistoryInterface* MakeMidasHistorySqlDebug()
2157 : {
2158 0 : return new SqlHistory(new SqlDebug());
2159 : }
2160 :
2161 : /* emacs
2162 : * Local Variables:
2163 : * tab-width: 8
2164 : * c-basic-offset: 3
2165 : * indent-tabs-mode: nil
2166 : * End:
2167 : */
|