LCOV - code coverage report
Current view: top level - src - history_odbc.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 978 0
Test Date: 2025-11-11 10:26:08 Functions: 0.0 % 66 0

            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              :  */
        

Generated by: LCOV version 2.0-1