00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include <stdio.h>
00013 #include <stdint.h>
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #include <ctype.h>
00017 #include <assert.h>
00018
00019 #include <vector>
00020 #include <string>
00021
00022
00023
00024
00025
00026 #include "midas.h"
00027 #include "history.h"
00028
00029
00030
00031
00032
00033 #define STRLCPY(dst, src) strlcpy((dst), (src), sizeof(dst))
00034 #define FREE(x) { if (x) free(x); (x) = NULL; }
00035
00036
00037
00038
00039
00040
00041
00042 static const int tid_size[] = {
00043 0,
00044 1,
00045 1,
00046 1,
00047 2,
00048 2,
00049 4,
00050 4,
00051 4,
00052 4,
00053 8,
00054 1,
00055 0,
00056 0,
00057 0,
00058 0,
00059 0
00060 };
00061
00062
00063 static const char *tid_name[] = {
00064 "NULL",
00065 "BYTE",
00066 "SBYTE",
00067 "CHAR",
00068 "WORD",
00069 "SHORT",
00070 "DWORD",
00071 "INT",
00072 "BOOL",
00073 "FLOAT",
00074 "DOUBLE",
00075 "BITFIELD",
00076 "STRING",
00077 "ARRAY",
00078 "STRUCT",
00079 "KEY",
00080 "LINK"
00081 };
00082
00083
00084 static const char *sql_type_pgsql[] = {
00085 "xxxINVALIDxxxNULL",
00086 "SMALLINT",
00087 "SMALLINT",
00088 "CHAR(1)",
00089 "SMALLINT",
00090 "SMALLINT",
00091 "INTEGER",
00092 "INTEGER",
00093 "BOOL",
00094 "FLOAT(53)",
00095 "FLOAT(53)",
00096 "SMALLINT",
00097 "VARCHAR",
00098 "xxxINVALIDxxxARRAY",
00099 "xxxINVALIDxxxSTRUCT",
00100 "xxxINVALIDxxxKEY",
00101 "xxxINVALIDxxxLINK"
00102 };
00103
00104 static const char *sql_type_mysql[] = {
00105 "xxxINVALIDxxxNULL",
00106 "tinyint unsigned",
00107 "tinyint",
00108 "char",
00109 "smallint unsigned",
00110 "smallint",
00111 "integer unsigned",
00112 "integer",
00113 "tinyint",
00114 "float",
00115 "double",
00116 "tinyint unsigned",
00117 "VARCHAR",
00118 "xxxINVALIDxxxARRAY",
00119 "xxxINVALIDxxxSTRUCT",
00120 "xxxINVALIDxxxKEY",
00121 "xxxINVALIDxxxLINK"
00122 };
00123
00124
00125
00126
00127
00128 static const char **sql_type = NULL;
00129
00130 static const char* midasTypeName(int tid)
00131 {
00132 assert(tid>=0);
00133 assert(tid<15);
00134 return tid_name[tid];
00135 }
00136
00137 static const char* midas2sqlType(int tid)
00138 {
00139 assert(tid>=0);
00140 assert(tid<15);
00141 return sql_type[tid];
00142 }
00143
00144 static int sql2midasType(const char* name)
00145 {
00146 for (int tid=0; tid<15; tid++)
00147 if (strcasecmp(name, sql_type[tid])==0)
00148 return tid;
00149 printf("sql2midasType: Cannot convert SQL data type \'%s\' to a MIDAS data type!\n", name);
00150 return 0;
00151 }
00152
00153 static bool isCompatible(int tid, const char* sqlType)
00154 {
00155 if (0)
00156 printf("compare types midas \'%s\'=\'%s\' and sql \'%s\'\n", midasTypeName(tid), midas2sqlType(tid), sqlType);
00157
00158 if (sql2midasType(sqlType) == tid)
00159 return true;
00160
00161 if (strcasecmp(midas2sqlType(tid), sqlType) == 0)
00162 return true;
00163
00164
00165 if (tid==TID_FLOAT && strcmp(sqlType, "double")==0)
00166 return true;
00167
00168
00169
00170 if (tid==TID_BYTE && strcmp(sqlType, "tinyint")==0)
00171 return true;
00172
00173
00174
00175 if (tid==TID_WORD && strcmp(sqlType, "tinyint")==0)
00176 return true;
00177
00178 return false;
00179 }
00180
00181
00182
00183
00184
00185 class SqlBase
00186 {
00187 public:
00188 virtual int SetDebug(int debug) = 0;
00189 virtual int Connect(const char* dsn = 0) = 0;
00190 virtual int Disconnect() = 0;
00191 virtual bool IsConnected() = 0;
00192 virtual int Exec(const char* sql) = 0;
00193 virtual int GetNumRows() = 0;
00194 virtual int GetNumColumns() = 0;
00195 virtual int Fetch() = 0;
00196 virtual int Done() = 0;
00197 virtual int ListTables(std::vector<std::string> *plist) = 0;
00198 virtual int ListColumns(const char* table, std::vector<std::string> *plist) = 0;
00199 virtual const char* GetColumn(int icol) = 0;
00200 virtual ~SqlBase() { };
00201 };
00202
00203
00204
00205
00206
00207 class SqlDebug: public SqlBase
00208 {
00209 public:
00210 FILE *fp;
00211 bool fIsConnected;
00212 int fDebug;
00213
00214 public:
00215
00216 SqlDebug()
00217 {
00218 fp = NULL;
00219 fIsConnected = false;
00220 }
00221
00222 ~SqlDebug()
00223 {
00224 if (fp)
00225 fclose(fp);
00226 fp = NULL;
00227 }
00228
00229 int SetDebug(int debug)
00230 {
00231 int old_debug = fDebug;
00232 fDebug = debug;
00233 return old_debug;
00234 }
00235
00236 int Connect(const char* filename = NULL)
00237 {
00238 if (!filename)
00239 filename = "/dev/fd/1";
00240 fp = fopen(filename, "w");
00241 assert(fp);
00242 sql_type = sql_type_mysql;
00243 fIsConnected = true;
00244 return DB_SUCCESS;
00245 }
00246
00247 int Exec(const char* sql)
00248 {
00249 fprintf(fp, "%s\n", sql);
00250 return DB_SUCCESS;
00251 }
00252
00253 int Disconnect()
00254 {
00255
00256 fIsConnected = false;
00257 return DB_SUCCESS;
00258 }
00259
00260 bool IsConnected()
00261 {
00262 return fIsConnected;
00263 }
00264
00265 int GetNumRows() { return DB_SUCCESS; }
00266 int GetNumColumns() { return DB_SUCCESS; }
00267 int Fetch() { return DB_NO_MORE_SUBKEYS; }
00268 int Done() { return DB_SUCCESS; }
00269 int ListTables(std::vector<std::string> *plist) { return DB_SUCCESS; };
00270 int ListColumns(const char* table, std::vector<std::string> *plist) { return DB_SUCCESS; };
00271 const char* GetColumn(int icol) { return NULL; };
00272 };
00273
00274 #ifdef HAVE_ODBC
00275
00276
00277
00278
00279
00280
00281
00282 #define DWORD DWORD_xxx
00283 #define BOOL BOOL_xxx
00284
00285 #include <sql.h>
00286 #include <sqlext.h>
00287 #include <sqltypes.h>
00288
00289
00290
00291
00292
00293 class SqlODBC: public SqlBase
00294 {
00295 public:
00296 bool fIsConnected;
00297
00298 std::string fDSN;
00299
00300 int fDebug;
00301
00302 SQLHENV fEnv;
00303 SQLHDBC fDB;
00304 SQLHSTMT fStmt;
00305
00306 SqlODBC();
00307 ~SqlODBC();
00308
00309 int SetDebug(int debug)
00310 {
00311 int old_debug = fDebug;
00312 fDebug = debug;
00313 return old_debug;
00314 }
00315
00316 int Connect(const char* dsn);
00317 int Disconnect();
00318 bool IsConnected();
00319
00320 int ListTables(std::vector<std::string> *plist);
00321 int ListColumns(const char* table_name, std::vector<std::string> *plist);
00322
00323 int Exec(const char* sql);
00324
00325 int GetNumRows();
00326 int GetNumColumns();
00327 int Fetch();
00328 const char* GetColumn(int icol);
00329 int Done();
00330
00331 protected:
00332 void ReportErrors(const char* from, const char* sqlfunc, int status);
00333 int DecodeError();
00334 };
00335
00336 SqlODBC::SqlODBC()
00337 {
00338 fIsConnected = false;
00339 fDebug = 0;
00340 }
00341
00342 SqlODBC::~SqlODBC()
00343 {
00344 Disconnect();
00345 }
00346
00347 int SqlODBC::Connect(const char* dsn)
00348 {
00349 if (fIsConnected)
00350 Disconnect();
00351
00352 fDSN = dsn;
00353
00354 int status = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fEnv);
00355
00356 if (!SQL_SUCCEEDED(status)) {
00357 cm_msg(MERROR, "SqlODBC::Connect", "SQLAllocHandle(SQL_HANDLE_ENV) error %d", status);
00358 return DB_FILE_ERROR;
00359 }
00360
00361 status = SQLSetEnvAttr(fEnv,
00362 SQL_ATTR_ODBC_VERSION,
00363 (void*)SQL_OV_ODBC2,
00364 0);
00365 if (!SQL_SUCCEEDED(status)) {
00366 cm_msg(MERROR, "SqlODBC::Connect", "SQLSetEnvAttr() error %d", status);
00367 SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
00368 return DB_FILE_ERROR;
00369 }
00370
00371 status = SQLAllocHandle(SQL_HANDLE_DBC, fEnv, &fDB);
00372 if (!SQL_SUCCEEDED(status)) {
00373 cm_msg(MERROR, "SqlODBC::Connect", "SQLAllocHandle(SQL_HANDLE_DBC) error %d", status);
00374 SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
00375 return DB_FILE_ERROR;
00376 }
00377
00378 SQLSetConnectAttr(fDB, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
00379
00380 if (0) {
00381
00382
00383 sql_type = sql_type_pgsql;
00384 status = SQLConnect(fDB, (SQLCHAR*) dsn, SQL_NTS,
00385 (SQLCHAR*) "xxx", SQL_NTS,
00386 (SQLCHAR*) "", SQL_NTS);
00387 }
00388
00389 if (1) {
00390
00391
00392 sql_type = sql_type_mysql;
00393 status = SQLConnect(fDB, (SQLCHAR*) dsn, SQL_NTS,
00394 (SQLCHAR*) NULL, SQL_NTS,
00395 (SQLCHAR*) NULL, SQL_NTS);
00396 }
00397
00398 if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
00399 SQLINTEGER V_OD_err;
00400 SQLSMALLINT V_OD_mlen;
00401 SQLCHAR V_OD_stat[10];
00402 SQLCHAR V_OD_msg[200];
00403
00404 SQLGetDiagRec(SQL_HANDLE_DBC, fDB, 1, V_OD_stat, &V_OD_err, V_OD_msg, 100, &V_OD_mlen);
00405 cm_msg(MERROR, "SqlODBC::Connect", "SQLConnect() error %d, %s (%d)", status, V_OD_msg,V_OD_err);
00406 SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
00407 return DB_FILE_ERROR;
00408 }
00409
00410 SQLAllocHandle(SQL_HANDLE_STMT, fDB, &fStmt);
00411
00412 if (fDebug)
00413 cm_msg(MINFO, "SqlODBC::Connect", "Connected to ODBC database DSN \'%s\'", dsn);
00414
00415 fIsConnected = true;
00416
00417 return DB_SUCCESS;
00418 }
00419
00420 int SqlODBC::Disconnect()
00421 {
00422 if (!fIsConnected)
00423 return DB_SUCCESS;
00424
00425 SQLDisconnect(fDB);
00426
00427 SQLFreeHandle(SQL_HANDLE_DBC, fDB);
00428 SQLFreeHandle(SQL_HANDLE_STMT, fStmt);
00429 SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
00430
00431 fIsConnected = false;
00432
00433 return DB_SUCCESS;
00434 }
00435
00436 bool SqlODBC::IsConnected()
00437 {
00438 return fIsConnected;
00439 }
00440
00441 void SqlODBC::ReportErrors(const char* from, const char* sqlfunc, int status)
00442 {
00443 if (fDebug)
00444 printf("%s: %s error %d\n", from, sqlfunc, status);
00445
00446 for (int i=1; ; i++) {
00447 SQLCHAR state[10];
00448 SQLINTEGER error;
00449 SQLCHAR message[1024];
00450 SQLSMALLINT mlen;
00451
00452 status = SQLGetDiagRec(SQL_HANDLE_STMT,
00453 fStmt,
00454 i,
00455 state,
00456 &error,
00457 message,
00458 sizeof(message),
00459 &mlen);
00460
00461 if (status == SQL_NO_DATA)
00462 break;
00463
00464 if (!SQL_SUCCEEDED(status)) {
00465 cm_msg(MERROR, "SqlODBC::ReportErrors", "SQLGetDiagRec() error %d", status);
00466 break;
00467 }
00468
00469 if (1 || (error != 1060) && (error != 1050)) {
00470 if (fDebug)
00471 printf("%s: %s error: state: \'%s\', message: \'%s\', native error: %d\n", from, sqlfunc, state, message, (int)error);
00472 cm_msg(MERROR, from, "%s error: state: \'%s\', message: \'%s\', native error: %d", sqlfunc, state, message, (int)error);
00473 }
00474 }
00475 }
00476
00477 int SqlODBC::DecodeError()
00478 {
00479
00480
00481
00482
00483
00484 for (int i=1; ; i++) {
00485 SQLCHAR state[10];
00486 SQLINTEGER error;
00487 SQLCHAR message[1024];
00488 SQLSMALLINT mlen;
00489
00490 error = 0;
00491
00492 int status = SQLGetDiagRec(SQL_HANDLE_STMT,
00493 fStmt,
00494 i,
00495 state,
00496 &error,
00497 message,
00498 sizeof(message),
00499 &mlen);
00500
00501 if (status == SQL_NO_DATA)
00502 return DB_SUCCESS;
00503
00504 if (error==1146)
00505 return DB_NO_KEY;
00506
00507 if (error==1050)
00508 return DB_KEY_EXIST;
00509 }
00510 }
00511
00512 int SqlODBC::ListTables(std::vector<std::string> *plist)
00513 {
00514 if (!fIsConnected)
00515 return DB_FILE_ERROR;
00516
00517 for (int i=0; i<2; i++) {
00518 if (fDebug)
00519 printf("SqlODBC::ListTables!\n");
00520
00521
00522 int status = SQLTables(fStmt, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR*)"TABLE", SQL_NTS);
00523
00524 if (SQL_SUCCEEDED(status))
00525 break;
00526
00527 if (fDebug)
00528 printf("SqlODBC::ListTables: SQLTables() error %d\n", status);
00529
00530 ReportErrors("SqlODBC::ListTables", "SQLTables()", status);
00531
00532 status = DecodeError();
00533
00534
00535
00536
00537
00538
00539
00540 cm_msg(MINFO, "SqlODBC::ListTables", "Reconnecting to ODBC database DSN \'%s\'", fDSN.c_str());
00541
00542
00543 std::string dsn = fDSN;
00544 Disconnect();
00545 status = Connect(dsn.c_str());
00546
00547 if (!fIsConnected) {
00548 cm_msg(MERROR, "SqlODBC::ListTables", "Cannot reconnect to ODBC database DSN \'%s\', status %d. Database is down?", fDSN.c_str(), status);
00549 return DB_FILE_ERROR;
00550 }
00551
00552 cm_msg(MINFO, "SqlODBC::ListTables", "Reconnected to ODBC database DSN \'%s\'", fDSN.c_str());
00553 }
00554
00555 int ncols = GetNumColumns();
00556 int nrows = GetNumRows();
00557
00558 if (ncols <= 0 || nrows <= 0) {
00559 cm_msg(MERROR, "SqlODBC::ListTables", "Error: SQLTables() returned unexpected number of columns %d or number of rows %d", ncols, nrows);
00560 }
00561
00562 int row = 0;
00563 while (1) {
00564 int status = Fetch();
00565 if (status != DB_SUCCESS)
00566 break;
00567
00568 if (0) {
00569 printf("row %d: ", row);
00570 for (int i=1; i<=ncols; i++) {
00571 const char* s = GetColumn(i);
00572 printf("[%s]", s);
00573 }
00574 printf("\n");
00575 row++;
00576 }
00577
00578 plist->push_back(GetColumn(3));
00579 }
00580
00581 Done();
00582
00583 return DB_SUCCESS;
00584 }
00585
00586 int SqlODBC::ListColumns(const char* table, std::vector<std::string> *plist)
00587 {
00588 if (!fIsConnected)
00589 return DB_FILE_ERROR;
00590
00591 for (int i=0; i<2; i++) {
00592 if (fDebug)
00593 printf("SqlODBC::ListColumns for table \'%s\'\n", table);
00594
00595
00596 int status = SQLColumns(fStmt, NULL, 0, NULL, 0, (SQLCHAR*)table, SQL_NTS, NULL, 0);
00597
00598 if (SQL_SUCCEEDED(status))
00599 break;
00600
00601 if (fDebug)
00602 printf("SqlODBC::ListColumns: SQLColumns(%s) error %d\n", table, status);
00603
00604 ReportErrors("SqlODBC::ListColumns", "SQLColumns()", status);
00605
00606 status = DecodeError();
00607
00608
00609
00610
00611
00612
00613
00614 cm_msg(MINFO, "SqlODBC::ListColumns", "Reconnecting to ODBC database DSN \'%s\'", fDSN.c_str());
00615
00616
00617 std::string dsn = fDSN;
00618 Disconnect();
00619 status = Connect(dsn.c_str());
00620
00621 if (!fIsConnected) {
00622 cm_msg(MERROR, "SqlODBC::ListColumns", "Cannot reconnect to ODBC database DSN \'%s\', status %d. Database is down?", fDSN.c_str(), status);
00623 return DB_FILE_ERROR;
00624 }
00625
00626 cm_msg(MINFO, "SqlODBC::ListColumns", "Reconnected to ODBC database DSN \'%s\'", fDSN.c_str());
00627 }
00628
00629 int ncols = GetNumColumns();
00630 int nrows = GetNumRows();
00631
00632 if (ncols <= 0 ) {
00633 cm_msg(MERROR, "SqlODBC::ListColumns", "Error: SQLColumns(\'%s\') returned unexpected number of columns %d or number of rows %d", table, ncols, nrows);
00634 }
00635
00636
00637
00638 int row = 0;
00639 while (1) {
00640 int status = Fetch();
00641 if (status != DB_SUCCESS)
00642 break;
00643
00644 if (0) {
00645 printf("row %d: ", row);
00646 for (int i=1; i<=ncols; i++) {
00647 const char* s = GetColumn(i);
00648 printf("[%s]", s);
00649 }
00650 printf("\n");
00651 row++;
00652 }
00653
00654 plist->push_back(GetColumn(4));
00655 plist->push_back(GetColumn(6));
00656 }
00657
00658 Done();
00659
00660 return DB_SUCCESS;
00661 }
00662
00663 int SqlODBC::Exec(const char* sql)
00664 {
00665
00666
00667
00668
00669
00670 if (!fIsConnected)
00671 return DB_FILE_ERROR;
00672
00673 int status;
00674
00675 for (int i=0; i<2; i++) {
00676 if (fDebug)
00677 printf("SqlODBC::Exec: %s\n", sql);
00678
00679 status = SQLExecDirect(fStmt,(SQLCHAR*)sql,SQL_NTS);
00680
00681 if (SQL_SUCCEEDED(status)) {
00682 return DB_SUCCESS;
00683 }
00684
00685 if (fDebug)
00686 printf("SqlODBC::Exec: SQLExecDirect() error %d: SQL command: \"%s\"\n", status, sql);
00687
00688 ReportErrors("SqlODBC::Exec", "SQLExecDirect()", status);
00689
00690 status = DecodeError();
00691
00692 if (status == DB_NO_KEY)
00693 return status;
00694
00695 if (status == DB_KEY_EXIST)
00696 return status;
00697
00698 cm_msg(MINFO, "SqlODBC::Exec", "Reconnecting to ODBC database DSN \'%s\'", fDSN.c_str());
00699
00700
00701 std::string dsn = fDSN;
00702 Disconnect();
00703 status = Connect(dsn.c_str());
00704
00705 if (!fIsConnected) {
00706 cm_msg(MERROR, "SqlODBC::Exec", "Cannot reconnect to ODBC database DSN \'%s\', status %d. Database is down?", fDSN.c_str(), status);
00707 return DB_FILE_ERROR;
00708 }
00709
00710 cm_msg(MINFO, "SqlODBC::Exec", "Reconnected to ODBC database DSN \'%s\'", fDSN.c_str());
00711 }
00712
00713 return DB_SUCCESS;
00714 }
00715
00716 int SqlODBC::GetNumRows()
00717 {
00718 SQLLEN nrows = 0;
00719
00720 int status = SQLRowCount(fStmt, &nrows);
00721 if (!SQL_SUCCEEDED(status)) {
00722 ReportErrors("SqlODBC::GetNumRow", "SQLRowCount()", status);
00723 return -1;
00724 }
00725 return nrows;
00726 }
00727
00728 int SqlODBC::GetNumColumns()
00729 {
00730 SQLSMALLINT ncols = 0;
00731
00732 int status = SQLNumResultCols(fStmt, &ncols);
00733 if (!SQL_SUCCEEDED(status)) {
00734 ReportErrors("SqlODBC::GetNumColumns", "SQLNumResultCols()", status);
00735 return -1;
00736 }
00737 return ncols;
00738 }
00739
00740 int SqlODBC::Fetch()
00741 {
00742 int status = SQLFetch(fStmt);
00743
00744 if (status == SQL_NO_DATA)
00745 return DB_NO_MORE_SUBKEYS;
00746
00747 if (!SQL_SUCCEEDED(status)) {
00748 ReportErrors("SqlODBC::Fetch", "SQLFetch()", status);
00749 return DB_FILE_ERROR;
00750 }
00751
00752 return DB_SUCCESS;
00753 }
00754
00755 int SqlODBC::Done()
00756 {
00757 int status = SQLCloseCursor(fStmt);
00758 if (!SQL_SUCCEEDED(status)) {
00759 ReportErrors("SqlODBC::Done", "SQLCloseCursor()", status);
00760 return DB_FILE_ERROR;
00761 }
00762 return DB_SUCCESS;
00763 }
00764
00765 const char* SqlODBC::GetColumn(int icol)
00766 {
00767 static char buf[1024];
00768 SQLLEN indicator;
00769 int status = SQLGetData(fStmt, icol, SQL_C_CHAR, buf, sizeof(buf), &indicator);
00770
00771 if (!SQL_SUCCEEDED(status)) {
00772 return NULL;
00773 }
00774
00775 if (indicator == SQL_NULL_DATA)
00776 return NULL;
00777
00778 return buf;
00779 }
00780
00781 #endif
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791 struct Tag
00792 {
00793 std::string column_name;
00794 int offset;
00795 TAG tag;
00796 bool create;
00797 };
00798
00799 struct Event
00800 {
00801 std::string event_name;
00802 std::string table_name;
00803 std::vector<Tag> tags;
00804 bool active;
00805
00806 Event()
00807 {
00808 active = false;
00809 }
00810
00811 ~Event()
00812 {
00813 active = false;
00814 }
00815 };
00816
00817 static void PrintTags(int ntags, const TAG tags[])
00818 {
00819 for (int i=0; i<ntags; i++)
00820 printf("tag %d: %s %s[%d]\n", i, midasTypeName(tags[i].type), tags[i].name, tags[i].n_data);
00821 }
00822
00823 int WriteEvent(SqlBase* sql, Event *e, time_t t, const char*buf, int size)
00824 {
00825
00826
00827 int n = e->tags.size();
00828
00829 std::string tags;
00830 std::string values;
00831
00832
00833
00834
00835 for (int i=0; i<n; i++) {
00836 const Tag*t = &e->tags[i];
00837
00838 if (t) {
00839 int offset = t->offset;
00840 void* ptr = (void*)(buf+offset);
00841
00842 int arraySize = t->tag.n_data;
00843
00844 for (int j=0; j<arraySize; j++) {
00845 tags += ", ";
00846 values += ", ";
00847
00848 if (arraySize <= 1)
00849 tags += t->column_name;
00850 else {
00851 tags += t->column_name;
00852 char s[256];
00853 sprintf(s,"_%d", j);
00854 tags += s;
00855 }
00856
00857 char s[1024];
00858
00859 switch (t->tag.type) {
00860 default:
00861 sprintf(s, "unknownType%d", t->tag.type);
00862 break;
00863 case 1:
00864 sprintf(s, "%u",((uint8_t*)ptr)[j]);
00865 break;
00866 case 2:
00867 sprintf(s, "%d",((int8_t*)ptr)[j]);
00868 break;
00869 case 3:
00870 sprintf(s, "\'%c\'",((char*)ptr)[j]);
00871 break;
00872 case 4:
00873 sprintf(s, "%u",((uint16_t*)ptr)[j]);
00874 break;
00875 case 5:
00876 sprintf(s, "%d",((int16_t*)ptr)[j]);
00877 break;
00878 case 6:
00879 sprintf(s, "%u",((uint32_t*)ptr)[j]);
00880 break;
00881 case 7:
00882 sprintf(s, "%d",((int32_t*)ptr)[j]);
00883 break;
00884 case 8:
00885 sprintf(s, "%u",((uint32_t*)ptr)[j]);
00886 break;
00887 case 9:
00888 sprintf(s, "\'%.8g\'",((float*)ptr)[j]);
00889 break;
00890 case 10:
00891 sprintf(s, "\'%.16g\'",((double*)ptr)[j]);
00892 break;
00893 }
00894
00895 values += s;
00896 }
00897 }
00898 }
00899
00900
00901 char s[1024];
00902 strftime(s,sizeof(s)-1,"%Y-%m-%d %H:%M:%S.0",localtime(&t));
00903
00904 char sss[102400];
00905 sprintf(sss, "INSERT INTO %s (_t_time, _i_time%s) VALUES (\'%s\', \'%d\'%s);",
00906 e->table_name.c_str(),
00907 tags.c_str(),
00908 s,
00909 (int)t,
00910 values.c_str());
00911
00912 int status = sql->Exec(sss);
00913
00914 if (status != DB_SUCCESS) {
00915 return status;
00916 }
00917
00918 return HS_SUCCESS;
00919 }
00920
00921
00922
00923 static std::string MidasNameToSqlName(const char* s)
00924 {
00925 std::string out;
00926
00927 for (int i=0; s[i]!=0; i++) {
00928 char c = s[i];
00929 if (isalpha(c) || isdigit(c))
00930 out += tolower(c);
00931 else
00932 out += '_';
00933 }
00934
00935 return out;
00936 }
00937
00938 struct IndexEntryTag
00939 {
00940 std::string tag_name;
00941 std::string column_name;
00942 int timestamp;
00943 };
00944
00945 struct IndexEntry
00946 {
00947 std::string event_name;
00948 std::string table_name;
00949 int timestamp;
00950 std::vector<IndexEntryTag> tags;
00951 std::vector<TAG> tags_cache;
00952 };
00953
00954 std::vector<IndexEntry*> gHistoryIndex;
00955
00956 static void PrintIndex()
00957 {
00958 for (unsigned i=0; i<gHistoryIndex.size(); i++) {
00959 IndexEntry *e = gHistoryIndex[i];
00960
00961 printf("entry %d: [%s] [%s], time %d, tags\n", i, e->event_name.c_str(), e->table_name.c_str(), e->timestamp);
00962
00963 for (unsigned j=0; j<e->tags.size(); j++)
00964 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);
00965 }
00966 }
00967
00968 static IndexEntry* FindIndexByTableName(const char* table_name)
00969 {
00970 for (unsigned i=0; i<gHistoryIndex.size(); i++)
00971 if (equal_ustring(gHistoryIndex[i]->table_name.c_str(), table_name)) {
00972 return gHistoryIndex[i];
00973 }
00974 return NULL;
00975 }
00976
00977 static IndexEntry* FindIndexByEventName(const char* event_name)
00978 {
00979 for (unsigned i=0; i<gHistoryIndex.size(); i++)
00980 if (equal_ustring(gHistoryIndex[i]->event_name.c_str(), event_name)) {
00981 return gHistoryIndex[i];
00982 }
00983 return NULL;
00984 }
00985
00986 static IndexEntryTag* FindIndexByTagName(IndexEntry* ie, const char* tag_name)
00987 {
00988 for (unsigned i=0; i<ie->tags.size(); i++)
00989 if (equal_ustring(ie->tags[i].tag_name.c_str(), tag_name)) {
00990 return &ie->tags[i];
00991 }
00992 return NULL;
00993 }
00994
00995 static IndexEntryTag* FindIndexByColumnName(IndexEntry* ie, const char* column_name)
00996 {
00997 for (unsigned i=0; i<ie->tags.size(); i++)
00998 if (equal_ustring(ie->tags[i].column_name.c_str(), column_name)) {
00999 return &ie->tags[i];
01000 }
01001 return NULL;
01002 }
01003
01004 static int gHaveIndex = true;
01005 static int gHaveIndexAll = false;
01006
01007 static int gTrace = 0;
01008
01009 static int ReadIndex(SqlBase* sql, const char* event_name)
01010 {
01011 if (gTrace)
01012 printf("ReadIndex [%s]\n", event_name);
01013
01014 if (!gHaveIndex)
01015 return HS_FILE_ERROR;
01016
01017 if (gHaveIndexAll)
01018 return HS_SUCCESS;
01019
01020 if (gTrace)
01021 printf("ReadIndex: reading index for event [%s]\n", event_name);
01022
01023
01024
01025 char cmd[256];
01026
01027 if (event_name)
01028 sprintf(cmd, "SELECT event_name, table_name, tag_name, column_name, itimestamp FROM _history_index where event_name=\'%s\';", event_name);
01029 else
01030 sprintf(cmd, "SELECT event_name, table_name, tag_name, column_name, itimestamp FROM _history_index;");
01031
01032 int status = sql->Exec(cmd);
01033
01034 if (status == DB_NO_KEY) {
01035 gHaveIndex = false;
01036 return HS_FILE_ERROR;
01037 }
01038
01039 if (gTrace) {
01040 printf("ReadIndex: event %s, Read status %d, nrows: %d\n",
01041 event_name,
01042 status,
01043 sql->GetNumRows());
01044 }
01045
01046 if (status != SUCCESS)
01047 return HS_FILE_ERROR;
01048
01049 if (sql->GetNumRows() == 0) {
01050 sql->Done();
01051 return HS_FILE_ERROR;
01052 }
01053
01054 int nrows = sql->GetNumRows();
01055 int ncols = sql->GetNumColumns();
01056
01057 if (nrows == 0)
01058 return HS_SUCCESS;
01059
01060 if (gTrace)
01061 printf("ReadIndex: event %s, nrows: %d, ncols: %d\n",
01062 event_name,
01063 nrows, ncols);
01064
01065 if (nrows < 0)
01066 return HS_FILE_ERROR;
01067
01068 if (ncols < 1)
01069 return HS_FILE_ERROR;
01070
01071
01072 while (1) {
01073 status = sql->Fetch();
01074 if (status != DB_SUCCESS)
01075 break;
01076
01077 std::string xevent_name = sql->GetColumn(1);
01078
01079 const char* p = sql->GetColumn(2);
01080 if (p) {
01081 std::string xtable_name = p;
01082 std::string xtimestamp = sql->GetColumn(5);
01083 int timestamp = atoi(xtimestamp.c_str());
01084
01085 IndexEntry* ie = FindIndexByEventName(xevent_name.c_str());
01086 if (!ie) {
01087 ie = new IndexEntry;
01088 gHistoryIndex.push_back(ie);
01089 ie->timestamp = timestamp - 1;
01090 }
01091
01092 if (timestamp > ie->timestamp) {
01093 ie->event_name = xevent_name;
01094 ie->table_name = xtable_name;
01095 ie->timestamp = timestamp;
01096 }
01097
01098
01099 continue;
01100 }
01101
01102 p = sql->GetColumn(3);
01103 if (p) {
01104 std::string xtag_name = p;
01105 std::string xcolumn_name = sql->GetColumn(4);
01106 std::string xtimestamp = sql->GetColumn(5);
01107 int timestamp = atoi(xtimestamp.c_str());
01108
01109 IndexEntry* ie = FindIndexByEventName(xevent_name.c_str());
01110 if (!ie) {
01111 ie = new IndexEntry;
01112 gHistoryIndex.push_back(ie);
01113 ie->timestamp = 0;
01114 ie->event_name = xevent_name;
01115 }
01116
01117 bool found = false;
01118 for (unsigned j=0; j<ie->tags.size(); j++)
01119 if (ie->tags[j].tag_name == xtag_name) {
01120 if (timestamp > ie->tags[j].timestamp) {
01121 ie->tags[j].timestamp = timestamp;
01122 ie->tags[j].column_name = xcolumn_name;
01123 }
01124 found = true;
01125 break;
01126 }
01127
01128 if (!found) {
01129 IndexEntryTag it;
01130 it.tag_name = xtag_name;
01131 it.column_name = xcolumn_name;
01132 it.timestamp = timestamp;
01133 ie->tags.push_back(it);
01134 }
01135
01136
01137 continue;
01138 }
01139
01140 }
01141
01142 sql->Done();
01143
01144 gHaveIndex = true;
01145
01146 if (event_name == NULL)
01147 gHaveIndexAll = true;
01148
01149
01150
01151 return HS_SUCCESS;
01152 }
01153
01154
01155
01156
01157
01158 class SqlHistory: public MidasHistoryInterface
01159 {
01160 public:
01161 SqlBase *fSql;
01162 int fDebug;
01163 std::string fConnectString;
01164 int fConnectRetry;
01165 int fNextConnect;
01166 std::vector<Event*> fEvents;
01167 std::vector<std::string> fIndexEvents;
01168 bool fHaveIndex;
01169 bool fHaveXIndex;
01170
01171 SqlHistory(SqlBase* b)
01172 {
01173 fDebug = 0;
01174 fConnectRetry = 0;
01175 fNextConnect = 0;
01176 fSql = b;
01177 fHaveIndex = false;
01178 fHaveXIndex = false;
01179 }
01180
01181 ~SqlHistory()
01182 {
01183 hs_disconnect();
01184 delete fSql;
01185 fSql = NULL;
01186 }
01187
01188 int hs_set_debug(int debug)
01189 {
01190 int old = fDebug;
01191 fDebug = debug;
01192 gTrace = debug;
01193 fSql->SetDebug(debug);
01194 return old;
01195 }
01196
01197 int hs_connect(const char* connect_string)
01198 {
01199 if (fDebug)
01200 printf("hs_connect %s!\n", connect_string);
01201
01202 assert(fSql);
01203
01204 if (fSql->IsConnected())
01205 if (strcmp(fConnectString.c_str(), connect_string) == 0)
01206 return HS_SUCCESS;
01207
01208 hs_disconnect();
01209
01210 fConnectString = connect_string;
01211
01212 if (fDebug)
01213 printf("hs_connect: connecting to SQL database \'%s\'\n", fConnectString.c_str());
01214
01215 int status = fSql->Connect(fConnectString.c_str());
01216 if (status != DB_SUCCESS)
01217 return status;
01218
01219 std::vector<std::string> tables;
01220
01221 status = fSql->ListTables(&tables);
01222 if (status != DB_SUCCESS)
01223 return status;
01224
01225 for (unsigned i=0; i<tables.size(); i++) {
01226 if (tables[i] == "_history_index") {
01227 fHaveIndex = true;
01228 break;
01229 }
01230 }
01231
01232 return HS_SUCCESS;
01233 }
01234
01235 int hs_disconnect()
01236 {
01237 if (fDebug)
01238 printf("hs_disconnect!\n");
01239
01240 fSql->Disconnect();
01241
01242 hs_clear_cache();
01243
01244 return HS_SUCCESS;
01245 }
01246
01247 int Reconnect()
01248 {
01249 if (fDebug)
01250 printf("Reconnect to SQL database!\n");
01251
01252 fSql->Disconnect();
01253 fSql->Connect(fConnectString.c_str());
01254 if (!fSql->IsConnected()) {
01255 return HS_FILE_ERROR;
01256 }
01257
01258 return HS_SUCCESS;
01259 }
01260
01261
01262
01263
01264
01265 int hs_clear_cache()
01266 {
01267 if (fDebug)
01268 printf("hs_clear_cache!\n");
01269
01270 gHaveIndex = true;
01271 gHaveIndexAll = false;
01272 fHaveXIndex = false;
01273
01274 for (unsigned i=0; i<gHistoryIndex.size(); i++) {
01275 IndexEntry* ie = gHistoryIndex[i];
01276 delete ie;
01277 }
01278 gHistoryIndex.clear();
01279
01280 fIndexEvents.clear();
01281
01282 return HS_SUCCESS;
01283 }
01284
01285 int XReadIndex()
01286 {
01287 if (fHaveXIndex)
01288 return HS_SUCCESS;
01289
01290 if (fDebug)
01291 printf("XReadIndex!\n");
01292
01293 std::vector<std::string> tables;
01294
01295 int status = fSql->ListTables(&tables);
01296 if (status != DB_SUCCESS)
01297 return status;
01298
01299 for (unsigned i=0; i<tables.size(); i++) {
01300 if (tables[i] == "_history_index")
01301 continue;
01302
01303 IndexEntry* ie = NULL;
01304
01305 if (!ie) {
01306 ie = new IndexEntry;
01307
01308 ie->table_name = tables[i];
01309 ie->event_name = ie->table_name;
01310
01311 gHistoryIndex.push_back(ie);
01312 }
01313
01314 std::vector<std::string> columns;
01315
01316 status = fSql->ListColumns(ie->table_name.c_str(), &columns);
01317 if (status != DB_SUCCESS)
01318 return status;
01319
01320 for (unsigned int j=0; j<columns.size(); j+=2) {
01321 if (columns[j] == "_t_time")
01322 continue;
01323 if (columns[j] == "_i_time")
01324 continue;
01325
01326 IndexEntryTag t;
01327 t.column_name = columns[j];
01328 t.tag_name = t.column_name;
01329 t.timestamp = 0;
01330
01331 ie->tags.push_back(t);
01332 }
01333 }
01334
01335 fHaveXIndex = true;
01336
01337
01338
01339 return HS_SUCCESS;
01340 }
01341
01342
01343
01344
01345
01346 int hs_define_event(const char* event_name, int ntags, const TAG tags[])
01347 {
01348 int status;
01349
01350 if (fDebug) {
01351 printf("define event [%s] with %d tags:\n", event_name, ntags);
01352 PrintTags(ntags, tags);
01353 }
01354
01355
01356 for (unsigned int i=0; i<fEvents.size(); i++)
01357 if (fEvents[i])
01358 if (fEvents[i]->event_name == event_name) {
01359 if (fDebug)
01360 printf("deleting exising event %s\n", event_name);
01361 delete fEvents[i];
01362 fEvents[i] = NULL;
01363 }
01364
01365 Event* e = new Event();
01366
01367 e->event_name = event_name;
01368
01369 if (!fHaveIndex) {
01370 char buf[1024];
01371 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);");
01372 int status = fSql->Exec(buf);
01373 if (status == DB_KEY_EXIST)
01374 ;
01375 else if (status != DB_SUCCESS)
01376 return status;
01377 fHaveIndex = true;
01378 }
01379
01380 IndexEntry* ie = FindIndexByEventName(event_name);
01381
01382 if (!ie) {
01383 ReadIndex(fSql, event_name);
01384 ie = FindIndexByEventName(event_name);
01385 }
01386
01387 if (!ie) {
01388 std::string table_name = MidasNameToSqlName(event_name);
01389
01390 double now = time(NULL);
01391
01392 char sss[102400];
01393 sprintf(sss, "INSERT INTO _history_index (event_name, table_name, itimestamp) VALUES (\'%s\', \'%s\', \'%.0f\');",
01394 event_name,
01395 table_name.c_str(),
01396 now);
01397
01398 int status = fSql->Exec(sss);
01399 if (status != DB_SUCCESS)
01400 return HS_FILE_ERROR;
01401
01402 ReadIndex(fSql, event_name);
01403 ie = FindIndexByEventName(event_name);
01404 }
01405
01406 if (!ie) {
01407 cm_msg(MERROR, "hs_define_event", "could not add event name to SQL history index table, see messages");
01408 return HS_FILE_ERROR;
01409 }
01410
01411 e->table_name = ie->table_name;
01412 e->active = true;
01413
01414 bool create_event = false;
01415
01416 int offset = 0;
01417 for (int i=0; i<ntags; i++) {
01418 for (unsigned int j=0; j<tags[i].n_data; j++) {
01419 std::string tagname = tags[i].name;
01420 std::string colname = MidasNameToSqlName(tags[i].name);
01421
01422 if (tags[i].n_data > 1) {
01423 char s[256];
01424 sprintf(s, "[%d]", j);
01425 tagname += s;
01426
01427 sprintf(s, "_%d", j);
01428 colname += s;
01429 }
01430
01431 IndexEntryTag *it = FindIndexByTagName(ie, tagname.c_str());
01432 if (!it) {
01433
01434
01435 while (1) {
01436 bool dupe = false;
01437
01438 for (unsigned i=0; i<e->tags.size(); i++) {
01439 if (colname == e->tags[i].column_name) {
01440
01441 dupe = true;
01442 break;
01443 }
01444 }
01445
01446 if (!dupe)
01447 break;
01448
01449 char s[256];
01450 sprintf(s, "_%d", rand());
01451 colname += s;
01452 }
01453
01454
01455
01456 double now = time(NULL);
01457
01458 char sss[102400];
01459 sprintf(sss, "INSERT INTO _history_index (event_name, tag_name, column_name, itimestamp) VALUES (\'%s\', \'%s\', \'%s\', \'%.0f\');",
01460 event_name,
01461 tagname.c_str(),
01462 colname.c_str(),
01463 now);
01464
01465 int status = fSql->Exec(sss);
01466 if (status != DB_SUCCESS)
01467 return HS_FILE_ERROR;
01468
01469
01470
01471 ReadIndex(fSql, event_name);
01472 ie = FindIndexByEventName(event_name);
01473 assert(ie);
01474 it = FindIndexByTagName(ie, tagname.c_str());
01475 }
01476
01477 if (!it) {
01478 cm_msg(MERROR, "hs_define_event", "could not add event tags to SQL history index table, see messages");
01479 return HS_FILE_ERROR;
01480 }
01481
01482 Tag t;
01483 t.column_name = it->column_name;
01484 t.create = false;
01485 t.offset = offset;
01486 t.tag = tags[i];
01487 t.tag.n_data = 1;
01488 e->tags.push_back(t);
01489 int size = tid_size[tags[i].type];
01490 offset += size;
01491 }
01492 }
01493
01494 std::vector<std::string> columns;
01495
01496 status = fSql->ListColumns(e->table_name.c_str(), &columns);
01497 if (status != DB_SUCCESS)
01498 return status;
01499
01500 if (columns.size() <= 0)
01501 create_event = true;
01502
01503 for (size_t i=0; i<e->tags.size(); i++) {
01504
01505 for (size_t j=i+1; j<e->tags.size(); j++)
01506 if (e->tags[i].column_name == e->tags[j].column_name) {
01507 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);
01508 e->active = false;
01509 break;
01510 }
01511
01512
01513 bool found = false;
01514 for (size_t j=0; j<columns.size(); j+=2) {
01515 if (e->tags[i].column_name == columns[j]) {
01516
01517
01518
01519 if (!isCompatible(e->tags[i].tag.type, columns[j+1].c_str())) {
01520 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());
01521 e->active = false;
01522 }
01523
01524 found = true;
01525 break;
01526 }
01527 }
01528
01529 if (!found) {
01530
01531
01532 e->tags[i].create = true;
01533 }
01534 }
01535
01536 if (create_event) {
01537 char buf[1024];
01538 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());
01539 status = fSql->Exec(buf);
01540 if (status != DB_SUCCESS) {
01541 e->active = false;
01542 return HS_FILE_ERROR;
01543 }
01544 }
01545
01546 for (size_t i=0; i<e->tags.size(); i++)
01547 if (e->tags[i].create) {
01548 char buf[1024];
01549
01550 sprintf(buf, "ALTER TABLE %s ADD COLUMN %s %s;",
01551 e->table_name.c_str(),
01552 e->tags[i].column_name.c_str(),
01553 midas2sqlType(e->tags[i].tag.type));
01554
01555 status = fSql->Exec(buf);
01556
01557 if (status != DB_SUCCESS) {
01558 e->active = false;
01559 return HS_FILE_ERROR;
01560 }
01561 }
01562
01563
01564 for (unsigned int i=0; i<fEvents.size(); i++)
01565 if (!fEvents[i]) {
01566 fEvents[i] = e;
01567 e = NULL;
01568 break;
01569 }
01570
01571
01572 if (e)
01573 fEvents.push_back(e);
01574
01575 return HS_SUCCESS;
01576 }
01577
01578 int hs_write_event(const char* event_name, time_t timestamp, int buffer_size, const char* buffer)
01579 {
01580 if (fDebug)
01581 printf("hs_write_event: write event \'%s\', time %d, size %d\n", event_name, (int)timestamp, buffer_size);
01582
01583
01584
01585 if (!fSql->IsConnected()) {
01586 time_t now = time(NULL);
01587
01588
01589 if (fConnectRetry !=0 && now < fNextConnect) {
01590 return HS_FILE_ERROR;
01591 }
01592
01593 cm_msg(MINFO, "hs_write_event", "Trying to reconnect to SQL database \'%s\'", fConnectString.c_str());
01594
01595 int status = fSql->Connect(fConnectString.c_str());
01596
01597 if (status != DB_SUCCESS) {
01598
01599
01600 if (fConnectRetry == 0)
01601 fConnectRetry = 5;
01602
01603 fNextConnect = now + fConnectRetry;
01604
01605
01606 fConnectRetry *= 2;
01607
01608
01609 if (fConnectRetry > 10*60)
01610 fConnectRetry = 10*60;
01611
01612 return HS_FILE_ERROR;
01613 }
01614
01615 cm_msg(MINFO, "hs_write_event", "Reconnected to SQL database \'%s\'", fConnectString.c_str());
01616 }
01617
01618 fNextConnect = 0;
01619 fConnectRetry = 0;
01620
01621 Event *e = NULL;
01622
01623
01624 for (size_t i=0; i<fEvents.size(); i++)
01625 if (fEvents[i]->event_name == event_name) {
01626 e = fEvents[i];
01627 break;
01628 }
01629
01630
01631 if (!e)
01632 return HS_UNDEFINED_EVENT;
01633
01634
01635 if (!e->active)
01636 return HS_FILE_ERROR;
01637
01638 int status = WriteEvent(fSql, e, timestamp, buffer, buffer_size);
01639
01640
01641 if (status != HS_SUCCESS) {
01642
01643
01644
01645 if (!fSql->IsConnected()) {
01646 return HS_FILE_ERROR;
01647 }
01648
01649
01650
01651 e->active = false;
01652
01653 cm_msg(MERROR, "hs_write_event", "Event \'%s\' disabled after write error %d into SQL database \'%s\'", event_name, status, fConnectString.c_str());
01654
01655 return HS_FILE_ERROR;
01656 }
01657
01658 return HS_SUCCESS;
01659 }
01660
01661
01662
01663
01664
01665 int hs_get_events(std::vector<std::string> *pevents)
01666 {
01667 if (fDebug)
01668 printf("hs_get_events!\n");
01669
01670 if (fIndexEvents.size() == 0) {
01671
01672 if (fDebug)
01673 printf("hs_get_events: reading event names!\n");
01674
01675 ReadIndex(fSql, NULL);
01676
01677 std::vector<std::string> tables;
01678 int status = fSql->ListTables(&tables);
01679 if (status != DB_SUCCESS)
01680 return status;
01681
01682 for (unsigned i=0; i<tables.size(); i++) {
01683 if (tables[i] == "_history_index")
01684 continue;
01685
01686 IndexEntry* ie = FindIndexByTableName(tables[i].c_str());
01687 if (!ie) {
01688 ReadIndex(fSql, NULL);
01689 ie = FindIndexByTableName(tables[i].c_str());
01690 }
01691
01692 if (ie)
01693 fIndexEvents.push_back(ie->event_name);
01694 else
01695 fIndexEvents.push_back(tables[i]);
01696 }
01697 }
01698
01699 assert(pevents);
01700 *pevents = fIndexEvents;
01701
01702 return HS_SUCCESS;
01703 }
01704
01705 int hs_get_tags(const char* event_name, std::vector<TAG> *ptags)
01706 {
01707 if (fDebug)
01708 printf("hs_get_tags for [%s]\n", event_name);
01709
01710 assert(ptags);
01711
01712 IndexEntry* ie = FindIndexByEventName(event_name);
01713
01714 if (!ie) {
01715 ReadIndex(fSql, event_name);
01716 ie = FindIndexByEventName(event_name);
01717 }
01718
01719 if (!ie) {
01720 XReadIndex();
01721 ie = FindIndexByEventName(event_name);
01722 }
01723
01724 if (!ie)
01725 return HS_UNDEFINED_EVENT;
01726
01727 if (ie->tags_cache.size() == 0) {
01728 if (fDebug)
01729 printf("hs_get_tags reading tags for [%s]\n", event_name);
01730
01731 std::string tname = ie->table_name;
01732
01733 std::vector<std::string> columns;
01734
01735 int status = fSql->ListColumns(tname.c_str(), &columns);
01736 if (status != DB_SUCCESS)
01737 return status;
01738
01739 if (columns.size() < 1) {
01740 cm_msg(MERROR, "hs_get_tags", "Cannot get columns for table \'%s\', try to reconnect to the database", tname.c_str());
01741
01742 int status = Reconnect();
01743 if (status != HS_SUCCESS)
01744 return status;
01745
01746 columns.clear();
01747 status = fSql->ListColumns(tname.c_str(), &columns);
01748 if (status != DB_SUCCESS)
01749 return status;
01750 }
01751
01752 TAG* t = (TAG*)malloc(sizeof(TAG)*columns.size());
01753 assert(t);
01754
01755 for (unsigned int j=0; j<columns.size(); j+=2) {
01756 if (columns[j] == "_t_time")
01757 continue;
01758 if (columns[j] == "_i_time")
01759 continue;
01760
01761 IndexEntryTag* it = FindIndexByColumnName(ie, columns[j].c_str());
01762
01763 TAG t;
01764 if (it)
01765 STRLCPY(t.name, it->tag_name.c_str());
01766 else
01767 STRLCPY(t.name, columns[j].c_str());
01768 t.type = sql2midasType(columns[j+1].c_str());
01769 t.n_data = 1;
01770
01771 ie->tags_cache.push_back(t);
01772 }
01773 }
01774
01775 for (unsigned i=0; i<ie->tags_cache.size(); i++)
01776 ptags->push_back(ie->tags_cache[i]);
01777
01778 return HS_SUCCESS;
01779 }
01780
01781 int hs_read_old_style(double start_time, double end_time, double interval,
01782 const char* event_name, const char* tag_name, int var_index,
01783 int *num_entries,
01784 time_t** time_buffer, double**data_buffer)
01785 {
01786 if (fDebug) {
01787 printf("hs_read_old_style: event \"%s\", tag \"%s\"\n", event_name, tag_name);
01788 }
01789
01790 ReadIndex(fSql, NULL);
01791
01792 for (unsigned e=0; e<gHistoryIndex.size(); e++) {
01793
01794 const char* s = gHistoryIndex[e]->event_name.c_str();
01795
01796 bool match = false;
01797 for (int j=0; s[j]; j++) {
01798
01799 if ((event_name[j]==0) && (s[j]=='/')) {
01800 match = true;
01801 break;
01802 }
01803
01804 if ((event_name[j]==0) && (s[j]=='_')) {
01805 match = true;
01806 break;
01807 }
01808
01809 if (event_name[j]==0) {
01810 match = false;
01811 break;
01812 }
01813
01814 if (tolower(event_name[j]) != tolower(s[j])) {
01815 match = false;
01816 break;
01817 }
01818 }
01819
01820
01821
01822 if (match) {
01823 bool found_tag = false;
01824 IndexEntry *ie = gHistoryIndex[e];
01825 for (unsigned v=0; v<ie->tags.size(); v++) {
01826
01827 if (equal_ustring(tag_name, ie->tags[v].tag_name.c_str())) {
01828 found_tag = true;
01829 break;
01830 }
01831 }
01832
01833 if (!found_tag)
01834 match = false;
01835 }
01836
01837 if (match) {
01838 if (fDebug)
01839 printf("hs_read_old_style: event \"%s\", tag \"%s\", try matching event \'%s\'\n", event_name, tag_name, s);
01840
01841 int status = hs_read(start_time, end_time, interval,
01842 s, tag_name, var_index,
01843 num_entries,
01844 time_buffer, data_buffer);
01845
01846 if (status==HS_SUCCESS && *num_entries>0)
01847 return HS_SUCCESS;
01848 }
01849 }
01850
01851 return HS_UNDEFINED_VAR;
01852 }
01853
01854 int hs_read(double start_time, double end_time, double interval,
01855 const char* event_name, const char* tag_name, int tag_index,
01856 int *num_entries,
01857 time_t** time_buffer, double**data_buffer)
01858 {
01859 *num_entries = 0;
01860 *time_buffer = NULL;
01861 *data_buffer = NULL;
01862
01863 if (fDebug)
01864 printf("hs_read: event [%s], tag [%s], index %d, start %f, end %f, dt %f, interval %f, max points %f\n",
01865 event_name, tag_name, tag_index,
01866 start_time, end_time, end_time-start_time, interval, (end_time-start_time)/interval);
01867
01868 if (event_name==NULL)
01869 return HS_SUCCESS;
01870
01871 IndexEntry*ie = FindIndexByEventName(event_name);
01872
01873 if (!ie) {
01874 ReadIndex(fSql, event_name);
01875 ie = FindIndexByEventName(event_name);
01876 }
01877
01878 if (!ie) {
01879 XReadIndex();
01880 ie = FindIndexByEventName(event_name);
01881 }
01882
01883 IndexEntryTag *it = NULL;
01884
01885 if (ie)
01886 it = FindIndexByTagName(ie, tag_name);
01887
01888 if (ie && !it) {
01889 char xxx[256];
01890 sprintf(xxx, "%s[%d]", tag_name, tag_index);
01891 it = FindIndexByTagName(ie, xxx);
01892 }
01893
01894
01895
01896 bool oldStyleEventName = (strchr(event_name, '/')==NULL);
01897
01898 if (oldStyleEventName)
01899 if (!ie || !it) {
01900 return hs_read_old_style(start_time, end_time, interval,
01901 event_name, tag_name, tag_index,
01902 num_entries,
01903 time_buffer, data_buffer);
01904 }
01905
01906 if (!it)
01907 return HS_UNDEFINED_VAR;
01908
01909 assert(ie);
01910 assert(it);
01911
01912 std::string tname = ie->table_name;
01913 std::string cname = it->column_name;
01914
01915 char cmd[256];
01916 sprintf(cmd, "SELECT _i_time, %s FROM %s WHERE _i_time>=%.0f and _i_time<=%.0f ORDER BY _i_time;",
01917 cname.c_str(), tname.c_str(),
01918 start_time, end_time);
01919
01920 int status = fSql->Exec(cmd);
01921
01922 if (fDebug) {
01923 printf("hs_read: event \"%s\", tag \"%s\", index %d: Read table \"%s\" column \"%s\": status %d, nrows: %d, ncolumns: %d\n",
01924 event_name, tag_name, tag_index,
01925 tname.c_str(),
01926 cname.c_str(),
01927 status,
01928 fSql->GetNumRows(),
01929 fSql->GetNumColumns()
01930 );
01931 }
01932
01933 if (status != SUCCESS) {
01934 return HS_FILE_ERROR;
01935 }
01936
01937 if (fSql->GetNumRows() == 0) {
01938 fSql->Done();
01939
01940 if (oldStyleEventName) {
01941 return hs_read_old_style(start_time, end_time, interval,
01942 event_name, tag_name, tag_index,
01943 num_entries,
01944 time_buffer, data_buffer);
01945 }
01946
01947 return HS_SUCCESS;
01948 }
01949
01950 int nrows = fSql->GetNumRows();
01951 int ncols = fSql->GetNumColumns();
01952
01953 if (nrows < 0)
01954 return HS_FILE_ERROR;
01955
01956 if (ncols < 1)
01957 return HS_FILE_ERROR;
01958
01959 *num_entries = 0;
01960 *time_buffer = (time_t*)malloc(nrows * sizeof(time_t));
01961 *data_buffer = (double*)malloc(nrows * sizeof(double));
01962
01963
01964 int row = 0;
01965 time_t tt = 0;
01966 int ann = 0;
01967 double att = 0;
01968 double avv = 0;
01969 while (1) {
01970 status = fSql->Fetch();
01971 if (status != DB_SUCCESS)
01972 break;
01973
01974 time_t t = 0;
01975 double v = 0;
01976
01977 const char* timedata = fSql->GetColumn(1);
01978 if (timedata)
01979 t = atoi(timedata);
01980
01981 const char* valuedata = fSql->GetColumn(2);
01982 if (valuedata)
01983 v = atof(valuedata);
01984
01985 if (t < start_time || t > end_time)
01986 continue;
01987
01988
01989
01990
01991 if (tt == 0 || t >= tt + interval) {
01992
01993 if (ann > 0) {
01994 assert(row < nrows);
01995
01996 (*time_buffer)[row] = (time_t)(att/ann);
01997 (*data_buffer)[row] = avv/ann;
01998
01999 row++;
02000 (*num_entries) = row;
02001 }
02002
02003 ann = 0;
02004 att = 0;
02005 avv = 0;
02006 tt = t;
02007
02008 }
02009
02010 ann++;
02011 att += t;
02012 avv += v;
02013 }
02014
02015 if (ann > 0) {
02016 assert(row < nrows);
02017
02018 (*time_buffer)[row] = (time_t)(att/ann);
02019 (*data_buffer)[row] = avv/ann;
02020
02021 row++;
02022 (*num_entries) = row;
02023 }
02024
02025 fSql->Done();
02026
02027 if (fDebug)
02028 printf("hs_read: return %d entries\n", *num_entries);
02029
02030 return HS_SUCCESS;
02031 }
02032
02033 int hs_read(time_t start_time, time_t end_time, time_t interval,
02034 int num_var,
02035 const char* event_name[], const char* tag_name[], const int tag_index[],
02036 int num_entries[],
02037 time_t* time_buffer[], double* data_buffer[],
02038 int st[])
02039 {
02040 if (fDebug)
02041 printf("hs_read: %d variables\n", num_var);
02042
02043 if (!fSql->IsConnected())
02044 return HS_FILE_ERROR;
02045
02046 for (int i=0; i<num_var; i++) {
02047
02048 if (event_name[i]==NULL) {
02049 st[i] = HS_UNDEFINED_EVENT;
02050 num_entries[i] = 0;
02051 continue;
02052 }
02053
02054 st[i] = hs_read(start_time, end_time, interval,
02055 event_name[i], tag_name[i], tag_index[i],
02056 &num_entries[i],
02057 &time_buffer[i], &data_buffer[i]);
02058 }
02059
02060 return HS_SUCCESS;
02061 }
02062 };
02063
02064
02065
02066
02067
02068 #ifdef HAVE_ODBC
02069 MidasHistoryInterface* MakeMidasHistoryODBC()
02070 {
02071 return new SqlHistory(new SqlODBC());
02072 }
02073 #endif
02074
02075 MidasHistoryInterface* MakeMidasHistorySqlDebug()
02076 {
02077 return new SqlHistory(new SqlDebug());
02078 }
02079
02080