00001
00002
00003 #include <stdio.h>
00004 #include <stdint.h>
00005 #include <stdlib.h>
00006 #include <string.h>
00007 #include <ctype.h>
00008 #include <assert.h>
00009
00010 #include <vector>
00011 #include <string>
00012
00013
00014
00015
00016
00017 #include "midas.h"
00018 #include "history_odbc.h"
00019
00020
00021
00022
00023
00024
00025
00026 #define DWORD DWORD_xxx
00027 #define BOOL BOOL_xxx
00028
00029 #include <sql.h>
00030 #include <sqlext.h>
00031 #include <sqltypes.h>
00032
00033
00034
00035
00036
00037 #define STRLCPY(dst, src) strlcpy(dst, src, sizeof(dst))
00038 #define FREE(x) { if (x) free(x); x = NULL; }
00039
00040
00041
00042
00043
00044 static int gTrace = 0;
00045 static std::string gAlarmName;
00046 static std::string gOdbcDsn;
00047 static time_t gNextConnect = 0;
00048 static int gConnectRetry = 0;
00049
00050
00051
00052
00053
00054
00055
00056 static int tid_size[] = {
00057 0,
00058 1,
00059 1,
00060 1,
00061 2,
00062 2,
00063 4,
00064 4,
00065 4,
00066 4,
00067 8,
00068 1,
00069 0,
00070 0,
00071 0,
00072 0,
00073 0
00074 };
00075
00076
00077 static char *tid_name[] = {
00078 "NULL",
00079 "BYTE",
00080 "SBYTE",
00081 "CHAR",
00082 "WORD",
00083 "SHORT",
00084 "DWORD",
00085 "INT",
00086 "BOOL",
00087 "FLOAT",
00088 "DOUBLE",
00089 "BITFIELD",
00090 "STRING",
00091 "ARRAY",
00092 "STRUCT",
00093 "KEY",
00094 "LINK"
00095 };
00096
00097
00098 static char *sql_type_pgsql[] = {
00099 "xxxINVALIDxxxNULL",
00100 "SMALLINT",
00101 "SMALLINT",
00102 "CHAR(1)",
00103 "SMALLINT",
00104 "SMALLINT",
00105 "INTEGER",
00106 "INTEGER",
00107 "BOOL",
00108 "FLOAT(53)",
00109 "FLOAT(53)",
00110 "SMALLINT",
00111 "VARCHAR",
00112 "xxxINVALIDxxxARRAY",
00113 "xxxINVALIDxxxSTRUCT",
00114 "xxxINVALIDxxxKEY",
00115 "xxxINVALIDxxxLINK"
00116 };
00117
00118 static char *sql_type_mysql[] = {
00119 "xxxINVALIDxxxNULL",
00120 "tinyint unsigned",
00121 "tinyint",
00122 "char",
00123 "smallint unsigned",
00124 "smallint",
00125 "integer unsigned",
00126 "integer",
00127 "tinyint",
00128 "float",
00129 "double",
00130 "tinyint unsigned",
00131 "VARCHAR",
00132 "xxxINVALIDxxxARRAY",
00133 "xxxINVALIDxxxSTRUCT",
00134 "xxxINVALIDxxxKEY",
00135 "xxxINVALIDxxxLINK"
00136 };
00137
00138
00139
00140
00141
00142 static char **sql_type = NULL;
00143
00144 static const char* midasTypeName(int tid)
00145 {
00146 assert(tid>=0);
00147 assert(tid<15);
00148 return tid_name[tid];
00149 }
00150
00151 static const char* midas2sqlType(int tid)
00152 {
00153 assert(tid>=0);
00154 assert(tid<15);
00155 return sql_type[tid];
00156 }
00157
00158 static int sql2midasType(const char* name)
00159 {
00160 for (int tid=0; tid<15; tid++)
00161 if (strcasecmp(name, sql_type[tid])==0)
00162 return tid;
00163 printf("sql2midasType: Cannot convert SQL data type \'%s\' to a MIDAS data type!\n", name);
00164 return 0;
00165 }
00166
00167 static bool isCompatible(int tid, const char* sqlType)
00168 {
00169 if (0 && gTrace)
00170 printf("compare types midas \'%s\'=\'%s\' and sql \'%s\'\n", midasTypeName(tid), midas2sqlType(tid), sqlType);
00171
00172 if (sql2midasType(sqlType) == tid)
00173 return true;
00174
00175 if (strcasecmp(midas2sqlType(tid), sqlType) == 0)
00176 return true;
00177
00178
00179 if (tid==TID_FLOAT && strcmp(sqlType, "double")==0)
00180 return true;
00181
00182
00183
00184 if (tid==TID_BYTE && strcmp(sqlType, "tinyint")==0)
00185 return true;
00186
00187
00188
00189 if (tid==TID_WORD && strcmp(sqlType, "tinyint")==0)
00190 return true;
00191
00192 return false;
00193 }
00194
00195
00196
00197
00198
00199 class SqlBase
00200 {
00201 public:
00202 virtual int Connect(const char* dsn = 0) = 0;
00203 virtual int Disconnect() = 0;
00204 virtual bool IsConnected() = 0;
00205 virtual int Exec(const char* sql) = 0;
00206 virtual int GetNumRows() = 0;
00207 virtual int GetNumColumns() = 0;
00208 virtual int Fetch() = 0;
00209 virtual int Done() = 0;
00210 virtual std::vector<std::string> ListTables() = 0;
00211 virtual std::vector<std::string> ListColumns(const char* table) = 0;
00212 virtual const char* GetColumn(int icol) = 0;
00213 virtual ~SqlBase() { };
00214 };
00215
00216
00217
00218
00219
00220 class SqlStdout: public SqlBase
00221 {
00222 public:
00223 FILE *fp;
00224 bool fIsConnected;
00225
00226 public:
00227 int Connect(const char* filename = NULL)
00228 {
00229 if (!filename)
00230 filename = "/dev/fd/1";
00231 fp = fopen(filename, "w");
00232 assert(fp);
00233 sql_type = sql_type_mysql;
00234 fIsConnected = true;
00235 return 0;
00236 }
00237
00238 int Exec(const char* sql)
00239 {
00240 fprintf(fp, "%s\n", sql);
00241 return 0;
00242 }
00243
00244 int Disconnect()
00245 {
00246
00247 fIsConnected = false;
00248 return 0;
00249 }
00250
00251 bool IsConnected()
00252 {
00253 return fIsConnected;
00254 }
00255
00256 SqlStdout()
00257 {
00258 fp = NULL;
00259 fIsConnected = false;
00260 }
00261
00262 ~SqlStdout()
00263 {
00264 if (fp)
00265 fclose(fp);
00266 fp = NULL;
00267 }
00268
00269 int GetNumRows() { return 0; }
00270 int GetNumColumns() { return 0; }
00271 int Fetch() { return 0; }
00272 int Done() { return 0; }
00273 std::vector<std::string> ListTables() { std::vector<std::string> list; return list; };
00274 std::vector<std::string> ListColumns(const char* table) { std::vector<std::string> list; return list; };
00275 const char* GetColumn(int icol) { return NULL; };
00276 };
00277
00278
00279
00280
00281
00282 class SqlODBC: public SqlBase
00283 {
00284 public:
00285 bool fIsConnected;
00286
00287 std::string fDSN;
00288
00289 SQLHENV fEnv;
00290 SQLHDBC fDB;
00291 SQLHSTMT fStmt;
00292
00293 SqlODBC();
00294 ~SqlODBC();
00295
00296 int Connect(const char* dsn);
00297 int Disconnect();
00298 bool IsConnected();
00299
00300 std::vector<std::string> ListTables();
00301 std::vector<std::string> ListColumns(const char* table_name);
00302
00303 int Exec(const char* sql);
00304
00305 int GetNumRows();
00306 int GetNumColumns();
00307 int Fetch();
00308 const char* GetColumn(int icol);
00309 int Done();
00310
00311 protected:
00312 void ReportErrors(const char* from, const char* sqlfunc, int status);
00313 };
00314
00315 SqlODBC::SqlODBC()
00316 {
00317 fIsConnected = false;
00318 }
00319
00320 SqlODBC::~SqlODBC()
00321 {
00322 Disconnect();
00323 }
00324
00325 int SqlODBC::Connect(const char* dsn)
00326 {
00327 if (fIsConnected)
00328 Disconnect();
00329
00330 fDSN = dsn;
00331
00332 int status = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fEnv);
00333
00334 if (!SQL_SUCCEEDED(status)) {
00335 cm_msg(MERROR, "SqlODBC::Connect", "SQLAllocHandle(SQL_HANDLE_ENV) error %d", status);
00336 return -1;
00337 }
00338
00339 status = SQLSetEnvAttr(fEnv,
00340 SQL_ATTR_ODBC_VERSION,
00341 (void*)SQL_OV_ODBC2,
00342 0);
00343 if (!SQL_SUCCEEDED(status)) {
00344 cm_msg(MERROR, "SqlODBC::Connect", "SQLSetEnvAttr() error %d", status);
00345 SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
00346 return -1;
00347 }
00348
00349 status = SQLAllocHandle(SQL_HANDLE_DBC, fEnv, &fDB);
00350 if (!SQL_SUCCEEDED(status)) {
00351 cm_msg(MERROR, "SqlODBC::Connect", "SQLAllocHandle(SQL_HANDLE_DBC) error %d", status);
00352 SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
00353 exit(0);
00354 }
00355
00356 SQLSetConnectAttr(fDB, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
00357
00358 if (0) {
00359
00360
00361 sql_type = sql_type_pgsql;
00362 status = SQLConnect(fDB, (SQLCHAR*) dsn, SQL_NTS,
00363 (SQLCHAR*) "xxx", SQL_NTS,
00364 (SQLCHAR*) "", SQL_NTS);
00365 }
00366
00367 if (1) {
00368
00369
00370 sql_type = sql_type_mysql;
00371 status = SQLConnect(fDB, (SQLCHAR*) dsn, SQL_NTS,
00372 (SQLCHAR*) NULL, SQL_NTS,
00373 (SQLCHAR*) NULL, SQL_NTS);
00374 }
00375
00376 if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
00377 SQLINTEGER V_OD_err;
00378 SQLSMALLINT V_OD_mlen;
00379 SQLCHAR V_OD_stat[10];
00380 SQLCHAR V_OD_msg[200];
00381
00382 SQLGetDiagRec(SQL_HANDLE_DBC, fDB, 1, V_OD_stat, &V_OD_err, V_OD_msg, 100, &V_OD_mlen);
00383 cm_msg(MERROR, "SqlODBC::Connect", "SQLConnect() error %d, %s (%d)", status, V_OD_msg,V_OD_err);
00384 SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
00385 return -1;
00386 }
00387
00388 SQLAllocHandle(SQL_HANDLE_STMT, fDB, &fStmt);
00389
00390 cm_msg(MINFO, "SqlODBC::Connect", "history_odbc: Connected to %s", dsn);
00391
00392 if (gAlarmName.length()>0)
00393 al_reset_alarm(gAlarmName.c_str());
00394
00395 fIsConnected = true;
00396
00397 return 0;
00398 }
00399
00400 int SqlODBC::Disconnect()
00401 {
00402 if (!fIsConnected)
00403 return 0;
00404
00405 SQLDisconnect(fDB);
00406
00407 SQLFreeHandle(SQL_HANDLE_DBC, fDB);
00408 SQLFreeHandle(SQL_HANDLE_STMT, fStmt);
00409 SQLFreeHandle(SQL_HANDLE_ENV, fEnv);
00410
00411 fIsConnected = false;
00412
00413 if (gAlarmName.length() > 0) {
00414 char buf[256];
00415 sprintf(buf, "%s lost connection to the history database", gAlarmName.c_str());
00416 al_trigger_alarm(gAlarmName.c_str(), buf, "Alarm", "", AT_INTERNAL);
00417 }
00418
00419 return 0;
00420 }
00421
00422 bool SqlODBC::IsConnected()
00423 {
00424 return fIsConnected;
00425 }
00426
00427 void SqlODBC::ReportErrors(const char* from, const char* sqlfunc, int status)
00428 {
00429 if (gTrace)
00430 printf("%s: %s error %d\n", from, sqlfunc, status);
00431
00432 for (int i=1; ; i++) {
00433 SQLCHAR state[10];
00434 SQLINTEGER error;
00435 SQLCHAR message[1024];
00436 SQLSMALLINT mlen;
00437
00438 status = SQLGetDiagRec(SQL_HANDLE_STMT,
00439 fStmt,
00440 i,
00441 state,
00442 &error,
00443 message,
00444 sizeof(message),
00445 &mlen);
00446
00447 if (status == SQL_NO_DATA)
00448 break;
00449
00450 if (!SQL_SUCCEEDED(status)) {
00451 cm_msg(MERROR, "SqlODBC::ReportErrors", "SQLGetDiagRec() error %d", status);
00452 break;
00453 }
00454
00455 if (1 || (error != 1060) && (error != 1050)) {
00456 if (gTrace)
00457 printf("%s: %s error: state: \'%s\', message: \'%s\', native error: %d\n", from, sqlfunc, state, message, error);
00458 cm_msg(MERROR, from, "%s error: state: \'%s\', message: \'%s\', native error: %d", sqlfunc, state, message, error);
00459 }
00460 }
00461 }
00462
00463 std::vector<std::string> SqlODBC::ListTables()
00464 {
00465 std::vector<std::string> list;
00466
00467 if (!fIsConnected)
00468 return list;
00469
00470
00471 int status = SQLTables(fStmt, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR*)"TABLE", SQL_NTS);
00472 if (!SQL_SUCCEEDED(status)) {
00473 ReportErrors("SqlODBC::ListTables", "SQLTables()", status);
00474 return list;
00475 }
00476
00477 int ncols = GetNumColumns();
00478 int nrows = GetNumRows();
00479
00480 if (ncols <= 0 || nrows <= 0) {
00481 cm_msg(MERROR, "SqlODBC::ListTables", "Error: SQLTables() returned unexpected number of columns %d or number of rows %d, status %d", ncols, nrows, status);
00482 }
00483
00484 int row = 0;
00485 while (Fetch()) {
00486 if (0) {
00487 printf("row %d: ", row);
00488 for (int i=1; i<=ncols; i++) {
00489 const char* s = GetColumn(i);
00490 printf("[%s]", s);
00491 }
00492 printf("\n");
00493 row++;
00494 }
00495
00496 list.push_back(GetColumn(3));
00497 }
00498
00499 Done();
00500
00501 return list;
00502 }
00503
00504 std::vector<std::string> SqlODBC::ListColumns(const char* table)
00505 {
00506 std::vector<std::string> list;
00507
00508 if (!fIsConnected)
00509 return list;
00510
00511
00512 int status = SQLColumns(fStmt, NULL, 0, NULL, 0, (SQLCHAR*)table, SQL_NTS, NULL, 0);
00513 if (!SQL_SUCCEEDED(status)) {
00514 ReportErrors("SqlODBC::ListColumns", "SQLColumns()", status);
00515 return list;
00516 }
00517
00518 int ncols = GetNumColumns();
00519 int nrows = GetNumRows();
00520
00521 if (ncols <= 0 ) {
00522 cm_msg(MERROR, "SqlODBC::ListColumns", "Error: SQLColumns(\'%s\') returned unexpected number of columns %d or number of rows %d, status %d", table, ncols, nrows, status);
00523 }
00524
00525
00526
00527 int row = 0;
00528 while (Fetch()) {
00529 if (0) {
00530 printf("row %d: ", row);
00531 for (int i=1; i<=ncols; i++) {
00532 const char* s = GetColumn(i);
00533 printf("[%s]", s);
00534 }
00535 printf("\n");
00536 row++;
00537 }
00538
00539 list.push_back(GetColumn(4));
00540 list.push_back(GetColumn(6));
00541 }
00542
00543 Done();
00544
00545 return list;
00546 }
00547
00548 int SqlODBC::Exec(const char* sql)
00549 {
00550 if (!fIsConnected)
00551 return -1;
00552
00553 int status;
00554
00555 for (int i=0; i<2; i++) {
00556 if (gTrace)
00557 printf("SqlODBC::Exec: %s\n", sql);
00558
00559 status = SQLExecDirect(fStmt,(SQLCHAR*)sql,SQL_NTS);
00560
00561 if (SQL_SUCCEEDED(status)) {
00562 return 0;
00563 }
00564
00565 if (gTrace)
00566 printf("SqlODBC::Exec: SQLExecDirect() error %d: SQL command: \"%s\"\n", status, sql);
00567
00568 ReportErrors("SqlODBC::Exec", "SQLExecDirect()", status);
00569
00570 cm_msg(MINFO, "SqlODBC::Exec", "history_odbc: Trying to reconnect to %s", fDSN.c_str());
00571
00572
00573 std::string dsn = fDSN;
00574 Disconnect();
00575 Connect(dsn.c_str());
00576
00577 if (!fIsConnected) {
00578 cm_msg(MERROR, "SqlODBC::Exec", "history_odbc: Reconnect to %s failed. Database is down?", fDSN.c_str());
00579 }
00580 }
00581
00582 return 0;
00583 }
00584
00585 int SqlODBC::GetNumRows()
00586 {
00587 SQLINTEGER nrows = 0;
00588
00589 int status = SQLRowCount(fStmt, &nrows);
00590 if (!SQL_SUCCEEDED(status)) {
00591 ReportErrors("SqlODBC::GetNumRow", "SQLRowCount()", status);
00592 return -1;
00593 }
00594 return nrows;
00595 }
00596
00597 int SqlODBC::GetNumColumns()
00598 {
00599 SQLSMALLINT ncols = 0;
00600
00601 int status = SQLNumResultCols(fStmt, &ncols);
00602 if (!SQL_SUCCEEDED(status)) {
00603 ReportErrors("SqlODBC::GetNumColumns", "SQLNumResultCols()", status);
00604 return -1;
00605 }
00606 return ncols;
00607 }
00608
00609 int SqlODBC::Fetch()
00610 {
00611 int status = SQLFetch(fStmt);
00612
00613 if (status == SQL_NO_DATA)
00614 return 0;
00615
00616 if (!SQL_SUCCEEDED(status)) {
00617 ReportErrors("SqlODBC::Fetch", "SQLFetch()", status);
00618 return -1;
00619 }
00620
00621 return 1;
00622 }
00623
00624 int SqlODBC::Done()
00625 {
00626 int status = SQLCloseCursor(fStmt);
00627 if (!SQL_SUCCEEDED(status)) {
00628 ReportErrors("SqlODBC::Done", "SQLCloseCursor()", status);
00629 return -1;
00630 }
00631 return 0;
00632 }
00633
00634 const char* SqlODBC::GetColumn(int icol)
00635 {
00636 static char buf[1024];
00637 SQLINTEGER indicator;
00638 int status = SQLGetData(fStmt, icol, SQL_C_CHAR, buf, sizeof(buf), &indicator);
00639
00640 if (!SQL_SUCCEEDED(status)) {
00641 return NULL;
00642 }
00643
00644 if (indicator == SQL_NULL_DATA)
00645 return NULL;
00646
00647 return buf;
00648 }
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658 struct Tag
00659 {
00660 std::string column_name;
00661 int offset;
00662 TAG tag;
00663 bool create;
00664 };
00665
00666 struct Event
00667 {
00668 std::string event_name;
00669 std::string table_name;
00670 std::vector<Tag> tags;
00671 bool active;
00672 bool create;
00673
00674 Event()
00675 {
00676 active = false;
00677 create = false;
00678 }
00679
00680 ~Event()
00681 {
00682 create = false;
00683 active = false;
00684 }
00685 };
00686
00687 static void PrintTags(int ntags, const TAG tags[])
00688 {
00689 for (int i=0; i<ntags; i++)
00690 printf("tag %d: %s %s[%d]\n", i, midasTypeName(tags[i].type), tags[i].name, tags[i].n_data);
00691 }
00692
00693 int CreateEvent(SqlBase* sql, Event* e)
00694 {
00695 int status;
00696
00697 if (e->create) {
00698 char buf[1024];
00699 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());
00700 status = sql->Exec(buf);
00701 if (status != 0) {
00702 e->active = false;
00703 return -1;
00704 }
00705 }
00706
00707 for (size_t i=0; i<e->tags.size(); i++)
00708 if (e->tags[i].create) {
00709 char buf[1024];
00710
00711 sprintf(buf, "ALTER TABLE %s ADD COLUMN %s %s;",
00712 e->table_name.c_str(),
00713 e->tags[i].column_name.c_str(),
00714 midas2sqlType(e->tags[i].tag.type));
00715
00716 status = sql->Exec(buf);
00717
00718 if (status != 0) {
00719 e->active = false;
00720 return -1;
00721 }
00722 }
00723
00724 return 0;
00725 }
00726
00727 int WriteEvent(SqlBase* sql, Event *e, time_t t, const char*buf, int size)
00728 {
00729
00730
00731 int n = e->tags.size();
00732
00733 std::string tags;
00734 std::string values;
00735
00736
00737
00738
00739 for (int i=0; i<n; i++) {
00740 const Tag*t = &e->tags[i];
00741
00742 if (t) {
00743 int offset = t->offset;
00744 void* ptr = (void*)(buf+offset);
00745
00746 int arraySize = t->tag.n_data;
00747
00748 for (int j=0; j<arraySize; j++) {
00749 tags += ", ";
00750 values += ", ";
00751
00752 if (arraySize <= 1)
00753 tags += t->column_name;
00754 else {
00755 tags += t->column_name;
00756 char s[256];
00757 sprintf(s,"_%d", j);
00758 tags += s;
00759 }
00760
00761 char s[1024];
00762
00763 switch (t->tag.type) {
00764 default:
00765 sprintf(s, "unknownType%d", t->tag.type);
00766 break;
00767 case 1:
00768 sprintf(s, "%u",((uint8_t*)ptr)[j]);
00769 break;
00770 case 2:
00771 sprintf(s, "%d",((int8_t*)ptr)[j]);
00772 break;
00773 case 3:
00774 sprintf(s, "\'%c\'",((char*)ptr)[j]);
00775 break;
00776 case 4:
00777 sprintf(s, "%u",((uint16_t*)ptr)[j]);
00778 break;
00779 case 5:
00780 sprintf(s, "%d",((int16_t*)ptr)[j]);
00781 break;
00782 case 6:
00783 sprintf(s, "%u",((uint32_t*)ptr)[j]);
00784 break;
00785 case 7:
00786 sprintf(s, "%d",((int32_t*)ptr)[j]);
00787 break;
00788 case 8:
00789 sprintf(s, "%u",((uint32_t*)ptr)[j]);
00790 break;
00791 case 9:
00792 sprintf(s, "\'%.8g\'",((float*)ptr)[j]);
00793 break;
00794 case 10:
00795 sprintf(s, "\'%.16g\'",((double*)ptr)[j]);
00796 break;
00797 }
00798
00799 values += s;
00800 }
00801 }
00802 }
00803
00804
00805 char s[1024];
00806 strftime(s,sizeof(s)-1,"%Y-%m-%d %H:%M:%S.0",localtime(&t));
00807
00808 char sss[102400];
00809 sprintf(sss, "INSERT INTO %s (_t_time, _i_time%s) VALUES (\'%s\', \'%d\'%s);",
00810 e->table_name.c_str(),
00811 tags.c_str(),
00812 s,
00813 (int)t,
00814 values.c_str());
00815
00816 int status = sql->Exec(sss);
00817
00818 if (status != 0) {
00819 e->active = false;
00820 return -1;
00821 }
00822
00823 return 0;
00824 }
00825
00826
00827
00828
00829
00830 static SqlBase *gSql = NULL;
00831
00832 int hs_debug_odbc(int debug)
00833 {
00834 int old = gTrace;
00835 gTrace = debug;
00836 return old;
00837 }
00838
00839 int hs_set_alarm_odbc(const char* alarm_name)
00840 {
00841 if (alarm_name)
00842 gAlarmName = alarm_name;
00843 else
00844 gAlarmName = "";
00845 return HS_SUCCESS;
00846 }
00847
00848 int hs_connect_odbc(const char* odbc_dsn)
00849 {
00850 int status;
00851
00852 if (gSql && gSql->IsConnected())
00853 if (strcmp(gOdbcDsn.c_str(), odbc_dsn) == 0)
00854 return HS_SUCCESS;
00855
00856 if (gSql)
00857 hs_disconnect_odbc();
00858
00859 assert(!gSql);
00860
00861 gOdbcDsn = odbc_dsn;
00862
00863 if (gTrace)
00864 printf("hs_connect_odbc: set DSN to \'%s\'\n", gOdbcDsn.c_str());
00865
00866 if (gOdbcDsn[0] == '/')
00867 gSql = new SqlStdout();
00868 else
00869 gSql = new SqlODBC();
00870
00871 status = gSql->Connect(gOdbcDsn.c_str());
00872 if (status != 0)
00873 return HS_FILE_ERROR;
00874
00875 return HS_SUCCESS;
00876 }
00877
00878 int hs_disconnect_odbc()
00879 {
00880 if (gTrace)
00881 printf("hs_disconnect_odbc!\n");
00882
00883 if (gSql) {
00884 gSql->Disconnect();
00885 delete gSql;
00886 gSql = NULL;
00887 }
00888
00889 return HS_SUCCESS;
00890 }
00891
00892
00893
00894 static std::string tagName2columnName(const char* s)
00895 {
00896 char out[1024];
00897 int i;
00898 for (i=0; s[i]!=0; i++)
00899 {
00900 char c = s[i];
00901 if (isalpha(c) || isdigit(c))
00902 out[i] = tolower(c);
00903 else
00904 out[i] = '_';
00905 }
00906
00907 out[i] = 0;
00908 return out;
00909 }
00910
00911 static std::string eventName2tableName(const char* s)
00912 {
00913 return tagName2columnName(s);
00914 }
00915
00916
00917
00918
00919
00920 static std::vector<Event*> gEvents;
00921
00922 int hs_define_event_odbc(const char* event_name, const TAG tags[], int tags_size)
00923 {
00924 assert(gSql);
00925
00926 int ntags = tags_size/sizeof(TAG);
00927
00928 if (gTrace) {
00929 printf("define event [%s] with %d tags:\n", event_name, ntags);
00930 PrintTags(ntags, tags);
00931 }
00932
00933
00934 for (unsigned int i=0; i<gEvents.size(); i++)
00935 if (gEvents[i])
00936 if (gEvents[i]->event_name == event_name) {
00937 printf("deleting exising event %s\n", event_name);
00938 delete gEvents[i];
00939 gEvents[i] = NULL;
00940 }
00941
00942 Event* e = new Event();
00943
00944 e->event_name = event_name;
00945 e->table_name = eventName2tableName(event_name);
00946 e->active = true;
00947 e->create = false;
00948
00949 int offset = 0;
00950 for (int i=0; i<ntags; i++) {
00951 for (unsigned int j=0; j<tags[i].n_data; j++) {
00952 Tag t;
00953 t.create = false;
00954 if (tags[i].n_data == 1)
00955 t.column_name = tagName2columnName(tags[i].name);
00956 else {
00957 char s[256];
00958 sprintf(s, "_%d", j);
00959 t.column_name = tagName2columnName(tags[i].name) + s;
00960 }
00961 t.offset = offset;
00962 t.tag = tags[i];
00963 t.tag.n_data = 1;
00964 e->tags.push_back(t);
00965 int size = tid_size[tags[i].type];
00966 offset += size;
00967 }
00968 }
00969
00970 std::vector<std::string> columns = gSql->ListColumns(e->table_name.c_str());
00971
00972 if (columns.size() <= 0)
00973 e->create = true;
00974
00975 for (size_t i=0; i<e->tags.size(); i++) {
00976
00977 for (size_t j=i+1; j<e->tags.size(); j++)
00978 if (e->tags[i].column_name == e->tags[j].column_name) {
00979 cm_msg(MERROR, "hs_define_event_odbc", "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);
00980 e->active = false;
00981 break;
00982 }
00983
00984
00985 bool found = false;
00986 for (size_t j=0; j<columns.size(); j+=2) {
00987 if (e->tags[i].column_name == columns[j]) {
00988
00989
00990
00991 if (!isCompatible(e->tags[i].tag.type, columns[j+1].c_str())) {
00992 cm_msg(MERROR, "hs_define_event_odbc", "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());
00993 e->active = false;
00994 }
00995
00996 found = true;
00997 break;
00998 }
00999 }
01000
01001 if (!found) {
01002
01003
01004 e->tags[i].create = true;
01005 }
01006 }
01007
01008 int status = CreateEvent(gSql, e);
01009
01010 if (status != 0) {
01011
01012
01013 e->active = false;
01014
01015 if (gAlarmName.length() > 0) {
01016 char buf[256];
01017 sprintf(buf, "%s cannot define history event \'%s\', see messages", gAlarmName.c_str(), e->event_name.c_str());
01018 al_trigger_alarm(gAlarmName.c_str(), buf, "Alarm", "", AT_INTERNAL);
01019 }
01020 }
01021
01022
01023 for (unsigned int i=0; i<gEvents.size(); i++)
01024 if (!gEvents[i]) {
01025 gEvents[i] = e;
01026 e = NULL;
01027 break;
01028 }
01029
01030
01031 if (e)
01032 gEvents.push_back(e);
01033
01034 return HS_SUCCESS;
01035 }
01036
01037 int hs_write_event_odbc(const char* event_name, time_t timestamp, const char* buffer, int buffer_size)
01038 {
01039 if (gTrace)
01040 printf("write event [%s] size %d\n", event_name, buffer_size);
01041
01042 assert(gSql);
01043
01044
01045
01046 if (!gSql->IsConnected()) {
01047 time_t now = time(NULL);
01048
01049
01050 if (gConnectRetry!=0 && now < gNextConnect) {
01051 return HS_FILE_ERROR;
01052 }
01053
01054 int status = gSql->Connect(gOdbcDsn.c_str());
01055
01056 if (status != 0) {
01057
01058
01059 if (gConnectRetry == 0)
01060 gConnectRetry = 5;
01061
01062 gNextConnect = now + gConnectRetry;
01063
01064
01065 gConnectRetry *= 2;
01066
01067
01068 if (gConnectRetry > 10*60)
01069 gConnectRetry = 10*60;
01070
01071 return HS_FILE_ERROR;
01072 }
01073 }
01074
01075 gNextConnect = 0;
01076 gConnectRetry = 0;
01077
01078 Event *e = NULL;
01079
01080
01081 for (size_t i=0; i<gEvents.size(); i++)
01082 if (gEvents[i]->event_name == event_name) {
01083 e = gEvents[i];
01084 break;
01085 }
01086
01087
01088 if (!e)
01089 return HS_FILE_ERROR;
01090
01091
01092 if (!e->active)
01093 return HS_FILE_ERROR;
01094
01095 int status = WriteEvent(gSql, e, timestamp, buffer, buffer_size);
01096
01097
01098 if (status != 0) {
01099
01100
01101 if (!gSql->IsConnected()) {
01102 return HS_FILE_ERROR;
01103 }
01104
01105
01106
01107 e->active = 0;
01108
01109 if (gAlarmName.length() > 0) {
01110 char buf[256];
01111 sprintf(buf, "%s cannot write history event \'%s\', see messages", gAlarmName.c_str(), e->event_name.c_str());
01112 al_trigger_alarm(gAlarmName.c_str(), buf, "Alarm", "", AT_INTERNAL);
01113 }
01114
01115 return HS_FILE_ERROR;
01116 }
01117
01118 return HS_SUCCESS;
01119 }
01120
01121
01122
01123
01124
01125 int hs_get_tags_odbc(const char* event_name, int *n_tags, TAG **tags)
01126 {
01127 assert(gSql);
01128
01129 *n_tags = 0;
01130 *tags = NULL;
01131
01132 std::string tname = eventName2tableName(event_name);
01133
01134 std::vector<std::string> columns = gSql->ListColumns(tname.c_str());
01135
01136 if (columns.size() < 1) {
01137 cm_msg(MERROR, "hs_get_tags_odbc", "Cannot get columns for table \'%s\', try to reconnect to the database", tname.c_str());
01138
01139 gSql->Disconnect();
01140 gSql->Connect(gOdbcDsn.c_str());
01141 if (!gSql->IsConnected()) {
01142
01143 if (gAlarmName.length() > 0) {
01144 char buf[256];
01145 sprintf(buf, "%s lost connection to the history database", gAlarmName.c_str());
01146 al_trigger_alarm(gAlarmName.c_str(), buf, "Alarm", "", AT_INTERNAL);
01147 }
01148
01149 return HS_FILE_ERROR;
01150 }
01151
01152 columns = gSql->ListColumns(tname.c_str());
01153 }
01154
01155 TAG* t = (TAG*)malloc(sizeof(TAG)*columns.size());
01156 assert(t);
01157
01158 int n=0;
01159 for (unsigned int j=0; j<columns.size(); j+=2) {
01160 if (columns[j] == "_t_time")
01161 continue;
01162 if (columns[j] == "_i_time")
01163 continue;
01164 STRLCPY(t[n].name, columns[j].c_str());
01165 t[n].type = sql2midasType(columns[j+1].c_str());
01166 t[n].n_data = 1;
01167 n++;
01168 }
01169
01170 if (0) {
01171 printf("event [%s] table [%s], tags: %d\n", event_name, tname.c_str(), n);
01172 PrintTags(n, t);
01173 }
01174
01175 *n_tags = n;
01176 *tags = t;
01177
01178 return HS_SUCCESS;
01179 }
01180
01181 int hs_read_odbc(time_t start_time, time_t end_time, time_t interval,
01182 const char* event_name, const char* tag_name, int var_index,
01183 int *num_entries,
01184 time_t** time_buffer, double**data_buffer)
01185 {
01186 assert(gSql);
01187
01188 *num_entries = 0;
01189 *time_buffer = NULL;
01190 *data_buffer = NULL;
01191
01192
01193
01194 std::string ename = eventName2tableName(event_name);
01195 std::string tname = tagName2columnName(tag_name);
01196
01197 int status = 1;
01198
01199 std::vector<std::string> tables = gSql->ListTables();
01200
01201 if (tables.size() <= 1) {
01202 cm_msg(MERROR, "hs_read_odbc", "ListTables() returned nothing, trying to reconnect to the database");
01203
01204 gSql->Disconnect();
01205 gSql->Connect(gOdbcDsn.c_str());
01206 if (!gSql->IsConnected()) {
01207
01208 if (gAlarmName.length() > 0) {
01209 char buf[256];
01210 sprintf(buf, "%s lost connection to the history database", gAlarmName.c_str());
01211 al_trigger_alarm(gAlarmName.c_str(), buf, "Alarm", "", AT_INTERNAL);
01212 }
01213
01214 return HS_FILE_ERROR;
01215 }
01216
01217 tables = gSql->ListTables();
01218 }
01219
01220 int len = strlen(event_name);
01221 for (unsigned int i=0; i<tables.size(); i++) {
01222
01223
01224 const char* t = tables[i].c_str();
01225 const char* s = strstr(t, ename.c_str());
01226
01227 if (s==t && (t[len]=='_'||t[len]==0)) {
01228
01229 bool found = false;
01230 std::vector<std::string> columns = gSql->ListColumns(tables[i].c_str());
01231 for (unsigned int j=0; j<columns.size(); j+=2) {
01232
01233 if (columns[j] == tname) {
01234 found = true;
01235 break;
01236 }
01237 }
01238
01239 if (found) {
01240 char cmd[256];
01241 sprintf(cmd, "SELECT _i_time, %s FROM %s where _i_time>=%d and _i_time<=%d;",
01242 tname.c_str(), tables[i].c_str(),
01243 (int)start_time, (int)end_time);
01244
01245 status = gSql->Exec(cmd);
01246
01247 if (gTrace) {
01248 printf("hs_read_odbc: event %s, name %s, index %d: Read table %s: status %d, nrows: %d\n",
01249 event_name, tag_name, var_index,
01250 tables[i].c_str(),
01251 status,
01252 gSql->GetNumRows());
01253 }
01254
01255 if (status)
01256 continue;
01257
01258 if (gSql->GetNumRows() == 0) {
01259 gSql->Done();
01260 status = SQL_NO_DATA;
01261 continue;
01262 }
01263
01264 break;
01265 }
01266 }
01267 }
01268
01269 if (status == SQL_NO_DATA)
01270 return HS_SUCCESS;
01271
01272 if (status) {
01273 if (gTrace)
01274 printf("hs_read_odbc: event %s, name %s, index %d, could not find the right table? status %d\n",
01275 event_name, tag_name, var_index,
01276 status);
01277
01278 return HS_FILE_ERROR;
01279 }
01280
01281 int nrows = gSql->GetNumRows();
01282 int ncols = gSql->GetNumColumns();
01283
01284 if (nrows == 0)
01285 return HS_SUCCESS;
01286
01287 if (gTrace)
01288 printf("hs_read_odbc: event %s, name %s, index %d, nrows: %d, ncols: %d\n",
01289 event_name, tag_name, var_index,
01290 nrows, ncols);
01291
01292 if (nrows < 0)
01293 return HS_FILE_ERROR;
01294
01295 if (ncols < 1)
01296 return HS_FILE_ERROR;
01297
01298 *num_entries = 0;
01299 *time_buffer = (time_t*)malloc(nrows * sizeof(time_t));
01300 *data_buffer = (double*)malloc(nrows * sizeof(double));
01301
01302
01303 int row = 0;
01304 time_t tt = 0;
01305 int ann = 0;
01306 double att = 0;
01307 double avv = 0;
01308 while (gSql->Fetch()) {
01309 time_t t = 0;
01310 double v = 0;
01311
01312 const char* timedata = gSql->GetColumn(1);
01313 if (timedata)
01314 t = atoi(timedata);
01315
01316 const char* valuedata = gSql->GetColumn(2);
01317 if (valuedata)
01318 v = atof(valuedata);
01319
01320 if (t < start_time || t > end_time)
01321 continue;
01322
01323
01324
01325
01326 if (tt == 0 || t >= tt + interval) {
01327
01328 if (ann > 0) {
01329 assert(row < nrows);
01330
01331 (*time_buffer)[row] = (time_t)(att/ann);
01332 (*data_buffer)[row] = avv/ann;
01333
01334 row++;
01335 (*num_entries) = row;
01336 }
01337
01338 ann = 0;
01339 att = 0;
01340 avv = 0;
01341 tt = t;
01342
01343 }
01344
01345 ann++;
01346 att += t;
01347 avv += v;
01348 }
01349
01350 if (ann > 0) {
01351 assert(row < nrows);
01352
01353 (*time_buffer)[row] = (time_t)(att/ann);
01354 (*data_buffer)[row] = avv/ann;
01355
01356 row++;
01357 (*num_entries) = row;
01358 }
01359
01360 gSql->Done();
01361
01362 if (gTrace)
01363 printf("hs_read_odbc: return %d events\n", *num_entries);
01364
01365 return HS_SUCCESS;
01366 }
01367
01368