zoukankan      html  css  js  c++  java
  • ETL应用:一种处理接口的Pro*C实现方法

          2007年,当时项目所有ETL采用C编写,实现了ETL基本功能。当接口很多时,为保证文件获取效率,做好接口可配置;文件维护中经常会出现接口晚到情况,需要有一种方法能将接口晚到信息写入数据库,便于短信告警。当时刚学习Pro*C不久,就实现了该方法, 如下

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <unistd.h> 
    #include <string.h> 
    #include <time.h> 
    #include <sys/wait.h> 
    #include <errno.h> 
    #include <sys/types.h> 
    #include <limits.h> 
    #include <sqlda.h> 
    #include <sqlcpr.h> 
    /*定义数据库连接信息*/ 
    #define USERNAME    "masaetl" 
    #define PASSWORD    "masaetl" 
    #define ORACLESID   "DWDB2" 
    #define MAX_MSG_LENGTH 128 
    #define MAX_CMD_LENGTH 1024 
    #define  TRUE         1 
    /*定义告警文件生成目录*/ 
    const char *alarm_path="/ETL_FS/etlfs/interface/toptea"; 
    /*定义处理日期,内部链接的静态变量*/ 
    static char filedate[9]; 
    /*调试代码时使用*/ 
    #ifndef DEBUG 
    #define DEBUG   // 定义调试开关 
    #endif 
    /*SQL语句返回值定义*/ 
    /*定义返回值,成功为0,错误<0,警告>0  */ 
    #define SQLCODE   sqlca.sqlcode       
    /*SQL语句返回错误解释 */ 
    #define SQLERRMC  sqlca.sqlerrm.sqlerrmc     
    /*包含数据库信息*/ 
    EXEC SQL INCLUDE sqlca; 
    EXEC SQL INCLUDE sqlda; 
    
    /*SQL语句返回值定义*/ 
    /*定义返回值,成功为0,错误<0,警告>0*/ 
    #define SQLCODE   sqlca.sqlcode   
    /*SQL语句返回错误解释*/   
    #define SQLERRMC  sqlca.sqlerrm.sqlerrmc   
    /*执行应用程序说明*/ 
    /*getlinkdata程序每执行一次得到最后提供的晚到接口后都会执行*/ 
    const char  * getlinkfile="/ETL_FS/etlfs/interface/service/public/proc/getlinkdata"; 
    /*getlatefile.sh根据得到的晚到接口列表自动提取接口文件并进行链接*/ 
    const char  * getlatefile="/ETL_FS/etlfs/interface/service/public/proc/getlatefile.sh"; 
    void sqlerror();           /*处理SQL错误*/ 
    int ConnectDataBase();     /*链接数据库*/ 
    void DisConnectDataBase(); /*断开数据库连接*/ 
    char *getYestDate(char *szDate);   /*得到前一天的日期,格式"YYYYMMDD"*/ 
    char *getFileName(const char *szFilePath,char *szFileName); /*从字符串中得到文件名*/ 
    int getAlarmFile(const char *alarmFile);     /*生成告警文件*/ 
    char *substitute(char *src,const char *sMatch,const char *sReplace);    /*将源字符串中子字符串替换*/ 
    /************************************** 
    *** 功能: 提取运行在数据库级报错信息 
    *** 输入变量: 
    *** 输出变量: 
    ***************************************/ 
    void sqlerror() 
    { 
       /*为了避免错误处理时发生死循环,应给出此说明*/ 
       EXEC SQL WHENEVER SQLERROR CONTINUE; 
       printf("
     Oracle error detected: "); 
       printf("
    %s",SQLERRMC);  /*错误信息*/ 
       
       /*断开数据库链接*/ 
       EXEC SQL ROLLBACK WORK RELEASE; 
       exit(1); 
    } 
    /************************************** 
    *** 功能:  连接数据库 
    *** 输入变量: 
    *** 输出变量: -1 连接数据库失败 0 成功 
    ***************************************/ 
    int ConnectDataBase() 
    { 
      EXEC SQL BEGIN DECLARE SECTION; 
              char  username[8]; 
              char  password[11]; 
              char  oraclesid[7]; 
        EXEC SQL END DECLARE  SECTION; 
         
      strcpy(username,USERNAME);  /*用户名*/ 
       //username.len=strlen(username.arr); 
       
       strcpy(password,PASSWORD);  /*密码*/ 
       //password.len=strlen(password.arr); 
       
       strcpy(oraclesid,ORACLESID);  /*Oralce SID*/ 
       //oraclesid.len=strlen(oraclesid.arr); 
       
       /*链接数据库*/ 
       EXEC SQL CONNECT :username IDENTIFIED BY :password USING :oraclesid; 
       if( SQLCODE ) { 
         
         return -1; 
        } 
       return 0; 
    } 
    /************************************** 
    *** 功能:  断开数据库连接 
    *** 输入变量: 
    *** 输出变量: 
    ***************************************/ 
    void DisConnectDataBase() 
    { 
      /*断开连接*/ 
    EXEC SQL ROLLBACK WORK RELEASE; 
      exit(0); 
    } 
    /*************************************** 
    **  功能 :    得到前一天的日期 
    *** 输入变量: 
    *** 输出变量: 
    ****************************************/ 
    char *getYestDate(char *szDate) 
    { 
       time_t yest; 
       struct tm ptime={''}; 
       char szChar[5]; 
         
       //若输入的参数为空,默认提取系统前一天的日期 
       if ( *szDate =='' ){ 
           
          yest=time(NULL)-(time_t)(60*60*24); 
          strftime(szDate,9,"%Y%m%d",localtime(&yest)); 
           
          return szDate; 
         } 
       //提取年 
       memset(szChar,0,sizeof(szChar)); 
       strncpy(szChar,szDate,4); 
       ptime.tm_year=atoi(szChar)-1900; 
         
       //提取月 
       memset(szChar,0,sizeof(szChar)); 
       strncpy(szChar,szDate+4,2); 
       ptime.tm_mon=atoi(szChar)-1; 
         
       //提取日 
       memset(szChar,0,sizeof(szChar)); 
       strncpy(szChar,szDate+6,2); 
       ptime.tm_mday=atoi(szChar); 
         
       yest=mktime(&ptime)-(time_t)(60*60*24); 
       strftime(szDate,9,"%Y%m%d",localtime(&yest)); 
         
       return szDate; 
    } 
    /**************************************** 
    **  功能: 判断当日是否有接口文件晚到 
    **     有,则将接口晚到信息写入告警文件中 
    **  输入参数:  告警文件名称 
    **  输出参数:  接口晚到个数 
    *****************************************/ 
    int getAlarmFile(const char *alarmFile) 
    { 
        FILE *pf; 
         
        /*申明接口编号,接口处理日期*/ 
        EXEC SQL BEGIN DECLARE SECTION; 
              char szSql[MAX_CMD_LENGTH]; 
              char szDate[9]; 
              int v_count=0; 
              long order_id; 
        EXEC SQL END DECLARE SECTION; 
         
        /*得到日期*/ 
        strcpy(szDate,filedate); 
         
         /*连接数据库*/ 
        if( ConnectDataBase() <0 ){ 
         
             printf("连接数据库失败,失败原因[%d][%s]",SQLCODE,SQLERRMC); 
             return -1; 
        } 
         /*得到接口晚到个数*/ 
        EXEC SQL SELECT COUNT(DISTINCT A.ORDER_ID) INTO :v_count 
                      FROM  MASAETL.ETL_INTERFACE_ALARM A, 
                       (SELECT * FROM MASAETL.ETL_LINK_FILE WHERE FILEDATE=:szDate) B 
                  WHERE A.ORDER_ID=B.ORDER_ID(+) AND B.ORDER_ID IS NULL AND A.IF_ALARM=1; 
       
       /*检查SQL是否可以正常执行*/           
        if(SQLCODE) 
        { 
            printf("line%d:执行检查接口晚到数目SQL失败,错误号:[%d][%s]
    ", __LINE__, SQLCODE, SQLERRMC); 
            return 0; 
        } 
                 
        /*判断当日是否有接口晚到*/ 
        if ( v_count >0 ){ 
         
          /*只有在有接口未到时才生成告警文件*/ 
          if ( ( pf=fopen(alarmFile,"a+") ) ==NULL ){ 
           
                printf("创建当日告警文件失败.
    "); 
                exit(1); 
          } 
               
            /*申明游标*/ 
            EXEC SQL DECLARE later_cursor CURSOR FOR 
               SELECT  DISTINCT A.ORDER_ID 
                  FROM  MASAETL.ETL_INTERFACE_ALARM A, 
                       (SELECT * FROM MASAETL.ETL_LINK_FILE WHERE FILEDATE=:szDate) B 
                  WHERE A.ORDER_ID=B.ORDER_ID(+) AND B.ORDER_ID IS NULL AND A.IF_ALARM=1; 
             
             /*打开游标*/ 
             EXEC SQL OPEN  later_cursor; 
             EXEC SQL WHENEVER NOT FOUND DO break; 
             
             while ( TRUE ){ 
             
             /*提取记录*/         
             EXEC SQL FETCH later_cursor INTO :order_id; 
               
             /*任务记录完,退出while循环*/ 
             if ( SQLCODE == 1403 ){ 
               
                    printf("提取记录为空!!!!
    "); 
                    break; 
                } 
              else if (SQLCODE){ 
               
                    printf("无法正常取得接口编号,退出!!!!
    "); 
                    printf("错误原因 SQLCODE=[%d][%s]
    ", SQLCODE, SQLERRMC); 
                    continue; 
                } 
             /*将告警信息写入文件*/ 
             fprintf(pf,"%ld|",order_id); 
             
            } 
             /*关闭游标*/ 
             EXEC SQL CLOSE  later_cursor; 
             /*关闭文件*/ 
             fclose(pf); 
        } 
         
        /*断开数据库连接*/ 
        DisConnectDataBase(); 
             
        return v_count; 
    } 
    /**************************************** 
    **  功能:  将字符串中的模式字段替换成日期 
    **     字段,这里处理的是日接口,模式是固定的YYYYMMDD 
    **  输入参数:   
    **             src 需要替换的源字符串 
    **             subpattern  为YYYYMMDD 
    **             subdest     为日期变量filedate 
    **  输出参数:  替换后字符串 
    *****************************************/ 
    char *substitute(char *src,const char *sMatch,const char *sReplace) 
    { 
       int  stringLen; 
       char sNewString[MAX_MSG_LENGTH]; 
       char *FindPos =NULL; 
       
       FindPos=strstr(src, sMatch); 
       
       while( FindPos ){ 
         
          memset(sNewString, 0, sizeof(sNewString)); 
          stringLen = FindPos - src; 
          strncpy(sNewString, src, stringLen); 
          strcat(sNewString, sReplace); 
          strcat(sNewString, FindPos + strlen(sMatch)); 
          strcpy(src, sNewString); 
          FindPos = strstr(src, sMatch); 
       }     
       
       return src; 
    } 
    /*************************************** 
    **  功能 :    主程序 
    ****************************************/ 
    int main(int argc,char **argv) 
    { 
       char alarmfile[128];     /*告警文件格式,IYYYYMMDD.chk*/   
       const char *moduel="YYYYMMDD";  /*字符串替换模式*/ 
       char cmdString[MAX_CMD_LENGTH];            /*shell字符串*/ 
       
       pid_t pid;  /*子进程ID*/ 
       int status; /*子进程终止状态*/ 
       
       EXEC SQL BEGIN DECLARE SECTION; 
             char pDate[9]; 
             
             long order_id;                     /*接口编号*/ 
              char file_pattern[33];             /*文件模式*/ 
              char source_ip[33];                /*源文件IP地址*/ 
              char source_username[33];          /*源主机用户名*/           
              char source_password[33];          /*源主机用户密码*/ 
              char source_path[128];             /*源主机文件所在目录*/ 
              char destination_path[128];        /*目的主机文件目录*/ 
              char link_path[128];               /*目的文件主机链接目录*/ 
               
              short ind_file_pattern; 
              short ind_source_ip; 
              short ind_source_username; 
              short ind_source_password; 
              short ind_source_path; 
              short ind_destination_path; 
              short ind_link_path; 
       EXEC SQL END DECLARE SECTION; 
       
       /*得到处理日期,就是上一天的日期*/ 
       getYestDate(filedate); 
       strcpy(pDate,filedate); 
         
       sprintf(alarmfile,"%s/IB%s.chk",alarm_path,filedate); 
    #ifdef DEBUG 
        printf("告警文件名:[%s].
    ",alarmfile); 
    #endif 
       /*调用getlinkfile得到最新的系统文件信息*/ 
       if ( system(getlinkfile) <0 ){ 
         
         printf("执行getlinkfile应用程序失败.
    "); 
         exit(1); 
       } 
       
       /*使用子进程,第一次执行时将FTP下来后生成告警文件,后面无需再处理告警文件*/ 
       if( ( pid =fork() ) ==0 ){ 
         
          if ( access(alarmfile,F_OK) <0 ){ 
         
          if ( getAlarmFile(alarmfile) ==0 ){ 
           
            printf("当日无接口晚到.
    "); 
          } 
         }   
       } 
        /*等待子进程结束*/ 
        if( wait(&status)!=pid ){ 
         
         perror((char *)status); 
        } 
         
        /*连接数据库*/ 
       if( ConnectDataBase() <0 ){ 
         
             printf("连接数据库失败,失败原因[%d][%s]",SQLCODE,SQLERRMC); 
             return -1; 
        } 
         
       /*使用游标得到数据库记录*/ 
       EXEC SQL DECLARE ftp_cursor CURSOR FOR 
            SELECT  A.ORDER_ID, A.FILE_PATTERN,A.SOURCE_IP,A.SOURCE_USERNAME, 
                    A.SOURCE_PASSWORD,A.SOURCE_PATH,A.DESTINATION_PATH,A.LINK_PATH 
                 FROM  MASAETL.ETL_INTERFACE_ALARM A, 
                       (SELECT * FROM MASAETL.ETL_LINK_FILE WHERE FILEDATE=:pDate) B 
                  WHERE A.ORDER_ID=B.ORDER_ID(+) AND B.ORDER_ID IS NULL AND A.IF_CHECK=1; 
         
        /*检查SQL执行情况*/ 
        if( SQLCODE ) 
        { 
            printf("执行提取晚到接口SQL失败,失败原因:[%d][%s].
    ", SQLCODE, SQLERRMC); 
            exit(1); 
        } 
                       
       /*打开游标*/   
       EXEC SQL OPEN ftp_cursor; 
       EXEC SQL WHENEVER NOT FOUND DO break; 
                       
       while ( TRUE ) 
       { 
         /*提取记录*/ 
         EXEC SQL FETCH ftp_cursor INTO :order_id, :file_pattern:ind_file_pattern, :source_ip:ind_source_ip, 
                  :source_username:ind_source_username, :source_password:ind_source_password, 
                  :source_path:ind_source_path, :destination_path:ind_destination_path, :link_path:ind_link_path; 
                   
          /*任务记录完,退出while循环*/ 
           if ( SQLCODE ){ 
           
                printf("提取接口编号失败,错误原因 SQLCODE=[%d][%s]
    ", SQLCODE, SQLERRMC); 
                continue; 
            } 
                 
         /*判断记录的有效性*/ 
         if (ind_file_pattern==-1){ 
             
            printf("文件模式为空.
    "); 
            continue; 
         } 
         if (ind_source_ip==-1){ 
           
            printf("源IP地址为空.
    "); 
            continue; 
         } 
         if (ind_source_username==-1){ 
           
            printf("源主机用户名为空.
    "); 
            continue; 
         } 
         if (ind_source_password==-1){ 
           
            printf("源主机密码为空.
    "); 
            continue; 
         } 
         if (ind_source_path==-1){ 
           
            printf("源主机目录为空.
    "); 
            continue; 
         } 
         if (ind_destination_path==-1){ 
           
            printf("目的主机目录为空.
    "); 
            continue; 
         } 
         if (ind_link_path==-1){ 
           
            printf("目录链接目录为空.
    "); 
            continue; 
         } 
         
         /*对文件模式字段进行字符串替换*/ 
         substitute(file_pattern,moduel,filedate); 
         
         /*对源主机目录字段进行字符串替换*/ 
         substitute(destination_path,moduel,filedate); 
         
         /*执行shell程序ftp数据文件*/ 
         memset(cmdString,0,sizeof(cmdString)); 
         sprintf(cmdString,"%s %s %s %s %s %s %s %s",getlatefile,source_ip,source_username,source_password, 
                    source_path,file_pattern,destination_path,link_path); 
    #ifdef DEBUG 
        printf("生成提取接口文件命令[%s].
    ",cmdString); 
    #endif 
           
         /*执行文件提取及链接*/ 
         if ( system(cmdString) <0 ){ 
           
           printf("获取接口[%ld]文件失败.
    ",order_id); 
        } 
         
       } 
       /*关闭游标*/ 
       EXEC SQL CLOSE ftp_cursor; 
       
       /*断开数据库连接*/ 
       DisConnectDataBase(); 
       
       return 0; 
    }

    学无止境
  • 相关阅读:
    vue报错:Not Found
    使用Vue DevTools 插件报错的问题:Vue.js is detected on this page. Open DevTools and look for the Vue panel.
    Linux安装docker
    asp.net5.0源码(Microsoft github源码)
    探索Asp net core3中的 项目文件、Program.cs和通用host(转)
    c#中的sealed关键字
    .NET平台常见技术框架整理汇总
    疫情下孩子的网课
    常系数齐次线性递推
    【2021~2022】模拟赛乱写 Period I
  • 原文地址:https://www.cnblogs.com/tychyg/p/4868490.html
Copyright © 2011-2022 走看看