CHAPTER 4: 標準インターフェイス




4.1 はじめに

問い合わせ言語コマンドは、mscallルーチンを使って C 言語プログラム中に組み込むことができます。 このルーチンは、引数としてデータベースディレクトリと 問い合わせ言語コマンドを指定するだけです。

   mscall (database_directory, query_language_statement);

データベースディレクトリと問い合わせ言語コマンドの両方は文字列として 指定する必要があります。また、文字列は、アプリケーションプログラムによって 作成することができます。 このルーチンは、SQL ステートメント実行に成功した場合、"0"を返し、 失敗した場合は "0"以外の値を返します。 (失敗の原因として、問い合わせ言語コマンドの構文エラーまたは 無効なテーブルおよびアトリビュート名の指定が考えられます。)



4.2 ヘッダーファイル mscc.h

ヘッダーファイル "mscc.h"は、mscallを使用するすべての C 言語アプリケーションでインクルードする必要があります。 このファイルには、mscallを含む、すべてのホスト言語インターフェイス ルーチンによって使用する定数およびデータタイプが定義されています。 ヘッダーファイルの内容を以下に示します。



   /***********************************************************************
    *      (c) Copyright   Empress Software Inc. 1983, 2003
    ***********************************************************************/
   
   #ifndef __MSCC_H
   
   #define __MSCC_H
   
   #ifdef __cplusplus
   extern "C" {
   #endif
   
   #include        <stdio.h>
   #include        "c.h"
   
   #include        <misc/public.h>
   #include        <usrmp/msdsqldt.h>
   #include        <usrfns/dtparvar.hx>
   #include        <api/mrapi.hx>
   #include        <api/msapi.hx>
   #include        <api/mxapi.hx>
   #include        <misc/init.hx>
   #include        <misc/mscc_com.h>
   #include        <misc/mxe.h>
   #include        <misc/mxtrig.h>
   
   
   #ifdef __cplusplus
   }
   #endif                                          
   
   #endif  /* __MSCC_H */
                        

mscc.hファイルはバージョンによって異なることに注意してください。 このファイルは、Empressディレクトリの下の以下の場所に置かれます。



4.3 初期化とクリーンアップ

mscallを使ってプログラムを作成するには2つの方法があります。

  1. 通常のmainプロシージャをmsmainとすることができます。 これを使用する場合、初期化が不要でデフォルトのmainプロシージャが 含まれます。また、引数は通常のmainと同様、argcおよび argvを持っています。同様に通常のC exitプロシージャを msexitとします。

  2. ユーザー自身でmain プロシージャを書くことができ、 はじめにmsinitをコールし、 終了でmsexitをコールします。 こられの2つのプロシージャはさまざまな初期化およびクリーンアップ処理を実行します。

mscleanルーチンは、 プログラムを終了する直前にコールするプロシージャをスタック上に登録するために 使用することができます。



4.4 コンパイルとリンク

mscallを使用したプログラムは、"cc"ではなく、 "empcc"を使用してコンパイルする必要があります。 これはプログラムのコンパイル時に適切なEmpressライブラリに リンクすることを確実にします。 この点を除けば、"empcc""cc"は同じ働きをします。



4.5 標準インターフェイスを使用した例

以下の例のデータベース名は、repairsいう名前の論理データベースです。 データベースの物理位置は、システム上のどこに置いても構いません。

例 1

以下のプログラム"names.c"は、 personnelテーブルから名前を検索します。

   #include <mscc.h>
   #define DATABASE "repairs"
   msmain ()
   {
        mscall (DATABASE, "select name from personnel");
   }

例 2

プログラム"phone.c"は、 1人の従業員名を指定し、電話番号を検索します。

   #include <mscc.h>
   
   #define DATABASE "repairs"
   #define BUFSIZE  512
   
   msmain (argc, argv)
        int   argc;
        char  *argv[];
   {
        char *name, command[BUFSIZE];
        name = argv[1];
        sprintf (command,"
        select phone from personnel dump where name =\
             '%s'", name);
        mscall (DATABASE, command);
   }

例 3

プログラム"newphone.c"は、 指定した名前が1つだけであるかをチェックします。

   #include <mscc.h>
   
   #define DATABASE  "repairs"
   #define  BUFSIZE  512
   
   msmain (argc, argv)
        int   argc;
        char  *argv[];
   {
        char   command[BUFSIZE], *name;
   
        if  (argc != 2)
        {
             fprintf (stderr, "Usage: phone \
                  employee_name\n");
             msexit (1);
        }
        name = argv[1];
        sprintf (command,
                  "select phone from personnel\
                  dump where name =\
                  '%s'", name);
             mscall (DATABASE, command);
   }

例 4

次の例は、phone プログラムの対話的なバージョン"getphone.c"です。

   #include <mscc.h>
   
   #define     DATABASE   "repairs"
   #define     NOTHING    ""
   #define     BUFSIZE    512
   msmain()
   {
        char  name[BUFSIZE], command[BUFSIZE];
   
        printf ("\nFIND phone number GIVEN employee's\name.\n");
        printf ("To stop, enter \".q\"\n\n");
             for  ( ; ; )
             {
                  do
                  {
                       printf ("Employee name: ");
                       gets (name);
                       if  (strcmp (name, ".q") == 0 )
                            msexit (0);
   
                  } while  (strcmp (name, NOTHING) == 0 );
                  printf ("\n");
                  sprintf (command,
                  "select phone from\personnel dump\
                       where name = '%s', name);
                  mscall (DATABASE, command);
                  printf ("\n");
             }
   }

例 5

プログラム"employees.c"は、 personnelテーブルのすべての従業員の名前と電話番号を検索し、 適切なヘッダーとともにカウント数を出力します。

   #include <mscc.h>
   #define   DATABASE   "repairs"
   msmain()
   {
   
        printf ("Current Employees\n\n");
        mscall (DATABASE, "select name, phone from\personnel");
        printf ("Total Number of Employees = ");
        fflush (stdout);
        mscall (DATABASE, "select count from personnel\dump");
        printf ("\n");
   }

例 6

C プログラム "loans.c"は、 指定された各従業員のローン残高の詳細を出力します。

   #include <mscc.h>
   #define  DATABASE    "repairs"
   #define  BUFSIZE     512
   
   msmain (argc, argv)
        int   argc;
        char  *argv[];
   {
      char  command[BUFSIZE], newcommand[BUFSIZE],*name;
      int        i;
   
      if (argc < 2 )
      {
        print f("USAGE: %s employee's name\n", argv[0]);
        exit (1);
      }
      for (i = 1 ; i < argc ; i++ )
      {
         name = argv[i];
         printf ("Loans currently outstanding to\
              %s:\n\n", name);
         fflush (stdout);
         sprintf (command,
              "select date, amount from loans where \
              name = '%s'", name);
         mscall (DATABASE, command);
         printf ("\n");
         printf ("Total amount owing for %s: ", name);
         fflush (stdout);
         sprintf (command,
              "select sum (amount) from loans dump \
              where name = '%s'", name);
         mscall  (DATABASE, command);
         printf ("\n\n");
      }
      name = argv[1];
      sprintf (command,
             "select sum (amount) from loans dump where \
             name = '%s'" name);
      for  (i = 2 ; i < argc ; i++ )
      {
         name = argv[i];
         sprintf (newcommand, " or name = '%s'", name);
         strcat (command, newcommand);
      }
      printf ("\n\ntotal amount outstanding over ");
      printf ("all of the above:");
      fflush (stdout);
      mscall (DATABASE, command);
   }

このプロシージャは、これに対応するシェルプログラムと全く同じ処理結果を返します。

例 7

プログラム "menu.c"は、単純なメニュー操作を行うプログラムで、 標準 C インターフェイスによってEmpress 問い合わせ言語を実行し、 結果を表示します。 選択可能なコマンドは、以下になります。

  1. 従業員番号
  2. 電話番号
  3. クレジット制限
  4. 従業員レコード全体
  5. ローン残高の総額
   #include <mscc.h>
   #define   DATABASE  "repairs"
   #define   YES       '1'
   
   #define   NO        '0'
   #define   BUFSIZE   512
   
   #define   NAMESIZE  32 /* max length of ATTR names +1 */
   #define   NOTHING   ""
   #define   NUMBER    "n"
   #define   PHONE     "p"
   #define   CREDIT    "c"
   #define   RECORD    "r"
   #define   LOANS     "l"
   #define   QUIT      "q"
   
   char name[BUFSIZE];
   char clause[BUFSIZE];
   
   msmain ()
   {
        char   showmenu;
        char   command[BUFSIZE],
             attribute[NAMESIZE];
        char   table[NAMESIZE],
             query[BUFSIZE];
   
        printmenu ();
    
        for  ( ; ; )          /* loop through repeated queries */
        {
             strcpy (attribute, NOTHING);
             strcpy (clause, NOTHING);
             strcpy (table, "personnel");
   
             showmenu = NO;
             printf ("\nCommand: ");
             gets (command);
             if  (strcmp (command, NUMBER) == 0)
             {
                  strcpy (attribute, "number");
                  findname ();
             }
             else
                  if (strcmp (command, PHONE) == 0)
                  {
                       strcpy (attribute, "phone");
                  findname ();
             }
             else
                  if (strcmp (command, CREDIT) == 0)
                  {
                       strcpy (attribute, "credit_limit");
                       findname ();
             }
             else
                  if (strcmp (command, RECORD) == 0)
                  {
                       /* do nothing yet */
                  }
   
                  else
                       if (strcmp (command, LOANS) == 0)
                       {
                            strcpy (table, "loans");
                       }
                  else
                       if (strcmp (command, QUIT) == 0 )
                       {
                            msexit (0); /* normal termination */
                       }
                       else
                       {
                            showmenu = YES;
                            briefmenu ();
                       }
                  if  (showmenu == NO) /* continue with query */
                  {
                       sprintf (query, "select %s from %s %s",
                            attribute, table, clause);
                       mscall (DATABASE, query);
                  }
        }
   }
   printmenu ()
   {
   
        printf ("\n\n To select:\n\n");
        printf ("An employee's personnel number  %s\n", NUMBER);
        printf ("An employee's phone number      %s\n", PHONE);
        printf ("An employee's credit limit      %s\n", CREDIT);
        printf ("All personnel records           %s\n", RECORD);
        printf ("All loans outstanding           %s\n", LOANS);
        printf ("To leave the program            %s\n\n", QUIT);
   }
   briefmenu ()
   {
        printf ("\nPersonnel number  (%s), phone (%s), ",
             NUMBER, PHONE);
        printf ("credit limit  (%s), all records (%s), \n", CREDIT, 
             RECORD);
        printf ("all loans  (%s), or quit  (%s)\n",
             LOANS, QUIT);
   }
   findname ()
   {
        do
        {
             printf ("Enter employee name: ");
             gets (name);
        } while  (STRCMP (name, NOTHING) == 0);
        sprintf (clause, "where name = '%s'", name);
   }


4.6 検索した値の利用

問い合わせ言語よりも複雑な方法でアトリビュート値を扱う場合、 問い合わせ言語によって作成されたダンプファイルのアトリビュート値の ポインタを取得するために msgvinitおよびmsgvalルーチンを使用します。 msgvlineルーチンは、カレントレコード番号を返し、 msgplineルーチンは、ダンプファイルのカレント行数を返します。

msgvinitルーチンは、 fopenによって返されたEmpressダンプファイルへのポインタを その引数として使用し、このファイルから値を取得するための初期化を行います。 常にこのルーチンは値の取得の前にコールしなくてはなりません。

msgvalルーチンは引数を必要とせず、文字列のアトリビュート値を返します。 ファイルの終了では(char *)0を返し、行の終了 (LIST DUMPコマンドによってダンプファイルを作成した場合は 通常、レコードの終了になります。) では、(char *)-1を返します。

msgvalを連続的にコールすると、それごとにアトリビュート値が返され、 すべての値が検索されるまで、アトリビュートごと、またレコードごとに ダンプファイルを通じて処理されます。 例えば、loansテーブルから全レコードを検索し、利息を加えた総額を出力する場合、 1 レコードのすべての値を検出した時に行の終了をチェックし、 必要なアトリビュートごとにmsgvalをコールし、 msgvalが、(char *)0を戻すまで実行するためのループが必要になります。

msgvlineは、引数を必要とせず、 LONGINTEGER型としてダンプファイル のカレント行数を返します。 通常、DUMPコマンドによってファイルを作成した場合、 レコード数のカウントになり、 DUMP LISTコマンドによってファイルを作成した場合、 アトリビュート数のカウントを返します。

msgplineは、引数を必要とせず、使用された場合、 DUMPおよびLIST DUMPコマンドに関わらず LONGINTEGER型としてダンプファイル のカレントレコード番号を返します。

例 1

プログラム"statement.c"は、 ローンの利息計算を行い、指定された従業員の月別データの単純な出力をします。 dollar型アトリビュート値をfloat型で返すための有益なdollcvtルーチンを この例では含んでいます。

#include  <mscc.h>
#define   DATABASE   "repairs"
#define   TEMP       "msdump"
#define   RATE       0.02
#define   BUFSIZE    128

extern    double     dollcvt ();

msmain (argc, argv)
   int   argc;
   char  *argv[];
{
   FILE   *file;
   int    loan_no, counter;
   char   *name, *value;
   char   command[BUFSIZE];
   double amount, interest, total, sum, limit;

   for (counter = 1; counter < argc; counter++)
   {
      name = argv[counter];
      sprintf (command, "select credit_limit from \
           personnel dump into '%s' where \
           name = '%s'", TEMP, name);

      mscall (DATABASE, command);
      file = fopen (TEMP, "r");

      msgvinit (file);
      value = msgval ();
      fclose (file);

      if (value  == (char *)0)
      {
         printf ("\n\n*****  Cannot find any");
         printf ("record of employee '%s' in the database *****\n",
              name);
      }
      else
      {
         limit = dollcvt (value);
         sprintf (command, "select date, amount from loans dump\
              into '%s' where name = '%s'",
              TEMP, name);
         mscall (DATABASE, command);
         printf ("\n\n\n");
         printf ("Monthly Statement for %s\n", name);
         printf ("*********************************\n\n");
         printf ("Date     Amount   Interest Total\n\n");
         file = fopen (TEMP, "r");msgvinit (file);
         value = msgval ();/* get first date */
         if (value == (char *)0)
      {
         printf ("No loans outstanding\n\n");
         printf ("Credit limit = $%-.2f\n",limit);
         continue;
      }
      loan_no = sum = 0;
      for ( ; value != (char *)0 ; )
      {
         printf ("%-7s", value);  /* print date */
         value = msgval (); /* get amount */
         printf (" $%-9s", value); /* print amount */
         amount = dollcvt (value);
         interest = amount * RATE;
         total = amount + interest;
         printf ("$%-8.2f $%-8.2f\n", interest, total);
         sum = sum + total;
         value = msgval (); /* check end-of-line */
         if (value == (char *)-1)
         {
              loan_no++;
              value = msgval (); /* get next date */
            }
         }
         fclose (file);
         printf ("\nNumber of loans = %d \n", loan_no);
         printf ("Credit limit      = $%-2.f\n", limit);
         printf ("Total Debts       = $%-.2f\n", sum);
         if (sum >= limit)
         {
   printf ("*****  Please note: \ your credit limit is");
            printf ("exceeded *****\n");
         }
        }
     }
     unlink (TEMP);
}
double dollcvt (string)
     char    *string;
{
     extern   double   atof ();
     char     c, buffer [BUFSIZE], *buf_ptr;
     
     for (buf_ptr = buffer ;
          (c =  *string++) != '\0' ;
        )
     if (c != '$' &&
         c != '*' &&
         c != ' ' &&
         c != ',')
         *buf_ptr++ = c;

     *buf_ptr = '\0';
     return (atof (buffer));
}