Приложение 3. Пример работы с системой резервирования

В качестве примера клиентского приложения приводится листинг программы testrez.c, которая демонстрирует обработку ситуации отказа главного сервера и переход на работу с резервным сервером. Взаимодействие с СУБД ЛИНТЕР выполняется с помощью интерфейса нижнего уровня (call-интерфейса).

Программа создает таблицу TEST, состоящую из 20 записей типа INT, затем в режиме AUTOCOMMIT циклически выполняет следующие операции:

  1. открывает канал;

  2. выполняет выборку каждой записи с выводом на экран;

  3. увеличивает значение каждой записи на 1;

  4. закрывает канал.

При получении ошибки выводится соответствующее сообщение, и повторяется последняя итерация цикла.

В режиме резервирования (одновременно работают главный и резервный серверы) изменения таблицы на главном сервере передаются в БД на резервной машине.

Если принудительно остановить главный сервер, то программа получит соответствующий код завершения и повторит последнюю итерацию уже с резервной БД. Выведенные значения записей таблицы TEST должны совпадать с теми, что были перед остановом главного сервера, или быть больше.

#include < stdio.h >
#include < signal.h >
#include "intlib.h"
#include "errors.h"

#define RECORDCOUNT 20
struct ErrString
{
  int ErrCode;
  char *ErrString;
};

static int Cbrk = 0;

static struct ErrString ES[]=
{
  {ERR_RECV,       "receive error"},
  {ERR_SEND,       "send error"},
  {ERR_CONNECT,    "connect error"},
  {ERR_RESLINK,    "reserver link lost "},
  {ERR_SEARCH,     "reserved server search timeout"},
  {ERR_LINKDOWN,   "connection has been closed"},
  {ERRNMRKAN,      "wrong channel number"},
  {ERROPENQUE,     "linter not found"},
  {KernelShutdown, "linter kernel is shutdowning"},
  {FORCED_ROLLB,   "forced rollback"},
  {ChannelBusy,    "channel is busy"},
  {0," "}
};

static int TestError(int Error)
{
  if ( Error  >= 4000 && Error <  5000 )
    return 0;
  switch ( Error )
  {
    case ERRNMRKAN:
    case ERROPENQUE:
    case KernelShutdown:
    case FORCED_ROLLB:
    case ChannelBusy:
      return 0;
  }
  return 1;
}

static char  *ErrorString(int Err)
{
  struct ErrString *ESP=ES;

  while(ESP- >ErrCode)
  {
    if(ESP- >ErrCode==Err)
      return(ESP- >ErrString);
    ESP++;
  }

  return(ESP- >ErrString);
}

static int CloseChannel(TCBL *Cbl)
{
  memcpy(Cbl- >Command, "CLOS", 4);
  inter(Cbl, NULL, NULL, NULL, NULL);
  if ( Cbl- >CodErr )
  {
    printf("CLOSE error=%ld,%s\n", Cbl- >CodErr, ErrorString(Cbl- >CodErr));
    return -1;
  }
  return 0;
}

void SIGINTHandler(int Sig)
{
  printf("cbrk received\n");
  signal(SIGINT, SIGINTHandler);
  Cbrk = 1;
}

int main(int argc, char **argv)
{
  TCBL Cbl;
  int  i;
  int  AfterError = 0;

  signal(SIGINT, SIGINTHandler);

  /* Open channel */
  memset(&Cbl, 0, sizeof(Cbl));
  memcpy (Cbl.Command, "OPEN", 4);
  if(argc == 2)
    strncpy(Cbl.Node, argv[1], sizeof(Cbl.Node));
  inter(&Cbl, "SYSTEM/MANAGER8", NULL, NULL, NULL);
  if (Cbl.CodErr != 0)
  {
    printf("Channel Open error=%ld: %s\n", Cbl.CodErr, ErrorString(Cbl.CodErr));
    exit(1);
  }

  /* Create test table */
  memset(Cbl.Command, ' ', 4);
  inter(&Cbl, NULL, "CREATE TABLE TEST(I INTEGER);", NULL, NULL);
  if (Cbl.CodErr)
  {
    if(Cbl.CodErr == RELEXIST)
    {
      printf("table TEST already exist\n");
    }
    else
    {
      printf("Create Table error=%d:%s\n", Cbl.CodErr, ErrorString(Cbl.CodErr));
      CloseChannel(&Cbl);
      return -1;
    }
  }
  else
  {
    /* Full test table */
    for(i = 0; i <  RECORDCOUNT; i++)
    {
      char ComString[128];

      sprintf(ComString, "INSERT INTO Test VALUES (%d);", i);
      inter(&Cbl, NULL, ComString, NULL, NULL);
      if (Cbl.CodErr != 0)
      {
        printf("INSERT error=%d:%s\n", Cbl.CodErr, ErrorString(Cbl.CodErr));
        CloseChannel(&Cbl);
        return -1;
      }
    }
  }

  if ( CloseChannel(&Cbl) )
  {
    return -1;
  }

  while(!Cbrk)
  {
    int StrCnt = 0;

    /* Open channel */
    memcpy (Cbl.Command, "OPEN", 4);
    inter(&Cbl, "SYSTEM/MANAGER8", NULL, NULL, NULL);
    if (Cbl.CodErr != 0)
    {
      printf("Channel Open error=%ld:%s\n", Cbl.CodErr,ErrorString(Cbl.CodErr));
      if( TestError(Cbl.CodErr) == 0)
      {
        AfterError = 1;
        sleep(5);
        printf("try again\n");
        continue;
      }
      return -1;
    }

    /* Select all data */
    memcpy(Cbl.Command, "SLCT", 4);
    Cbl.PrzExe = M_BINARY;
    Cbl.LnBufRow = sizeof(int);
    inter(&Cbl,NULL,"SELECT I FROM TEST;", NULL,(HPCHAR)&i);
    if (Cbl.CodErr != 0)
    {
      printf("SELECT error=%ld:%s\n",Cbl.CodErr,ErrorString(Cbl.CodErr));
      if(TestError(Cbl.CodErr) == 0)
      {
        CloseChannel(&Cbl);
        continue;
      }
      CloseChannel(&Cbl);
      return -1;
    }

    /* Print data */
    printf("\n");
    while (Cbl.CodErr == 0)
    {
      StrCnt++;
      if ( StrCnt == 7 )
      {
        printf("\n");
        StrCnt=1;
      }
      printf(" i=%d", i);

      memcpy(Cbl.Command ,"GETN" ,4); /* Get Next Answer */
      inter(&Cbl ,NULL ,NULL ,NULL ,(HPCHAR)&i);
    } /* while */

    if (Cbl.CodErr == 2) /* End of Answer */
      printf("\n no more records\n");
    else
    {
      if ( TestError(Cbl.CodErr) == 0 )
      {
        CloseChannel(&Cbl);
        continue;
      }
      else
      {
        CloseChannel(&Cbl);
        return -1;
      }
    }

    /* Wait for compare */
    if(AfterError)
    {
      printf("press key to continue\n");
      getchar();
    }
    AfterError = 0;

    /* Update data */
    memset(Cbl.Command, ' ', 4); /* Command is not Select ! */
    inter(&Cbl, NULL,"update TEST set I = I+1;", NULL, NULL);
    if (Cbl.CodErr != 0)
    {
      printf("update error=%ld:%s\n", Cbl.CodErr,ErrorString(Cbl.CodErr));
      if ( TestError(Cbl.CodErr) == 0 )
      {
        CloseChannel(&Cbl);
        continue;
      }
      else
      {
        CloseChannel(&Cbl);
        return -1;
      }
    }

    if (CloseChannel(&Cbl))
    {
      continue;
    }
  }

  return 0;
}