# Chapter 3: サンプルプログラム

## 3.1 はじめに

この章では、mrルーチンの使用方法をさらに詳細に解説し、多数のサンプルプログラムを提供します。個々のサンプルは コンパイル、実行可能な完全なプログラムです。前半のサンプルは有益なものではありませんが、後半のサンプルは 前半のサンプルに基づいて作成されます。

このマニュアルのサンプルプログラムでは、論理的なデータベース名としてrepairsを使用します。 データベースの物理的な位置は、システムのいかなる場所であってもかまいません。

## 3.2 テーブルのオープンとクローズ

テーブルへのアクセスの第一ステップは、テーブル記述子を取得することです。mropen関数が、テーブルをオープンし テーブル記述子を返します。テーブルをオープンできない場合には、呼出しプログラムを終了します。mropen関数では、 データディクショナリーで設定されているレベルの、ロックをテーブルに設定します。ここでテーブルレベルロックが設定 されていれば、テーブル全体をロックしようとします。その時に既にロック中であれば、変数MSLOCKRETRYとMSLOCKSLEEP に設定されている値によって、再度ロックを実行します。テーブルをオープンした後で、ロックを設定できない場合には、 エラーメッセージを出力し、呼出しプログラムを終了します。

上記2つの関数の、一般的な形式を示します。

       table_desc = mropen (database_name, table_name, mode);

       table_desc = mrtopen (database_name, table_name, mode);

テーブルは、その内容を保護する、readモード('r')、ロックを回避する、 ダーティリードモード('n')、変更や削除を行なう 為の、deferredモード('d')、updateモード('u')を指定してオープンされます。Empressテーブルは、通常は作成者以外からの 書込み保護の状態で、作成されるため、テーブルの作成者でないユーザが、updateモードでオープンしようとすると、 失敗します。テーブルの内容を検索するが、更新をしないアプリケーションでは、上記の問題を回避するためには、readモード でオープンする必要があります。

mropen関数とmrtopen関数の呼出しは、データディクショナリーをオープンして、アクセス特権とロックレベルに関する情報を 取得し、データディクショナリーをクローズします。ただし、データディクショナリーが既にオープンされている場合には、 データベース名の代わりに、CHARNILを指定できます。これにより、データディクショナリーの、オープン／クローズの処理が 実行されないので、テーブルのオープンに対する処理時間が、短縮されます。

意図する、すべての操作がテーブルに対して実行された後は、テーブルをmrclose関数でクローズします。この関数は 、テーブルがロックされている場合には、そのロックの解除も行います。以下に、一般的な形式を示します。

       mrclose (table_desc);

例

以下に示すプログラム、open.cは、データベースrepairsにある、loansテーブルを、updateモードでオープンして、 テーブル記述子を取得し、クローズします。

``

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr     loans_tabdesc;

            loans_tabdesc = mropen (DATABASE, "loans", 'u');
            mrclose (loans_tabdesc);
       }

## 3.3 データディクショナリーのオープン

mropdict関数またはmrtopdict関数は、データディクショナリーを構成する、すべてのテーブルをオープンします。 mrcldict関数は、データディクショナリーをクローズします。一般的な形式を以下に示します。

       mropdict (database_name, mode);

       flag = mrtopdict (database_name, mode);

       mrcldict ();

modeは、'r'または'u'を指定します。'r'はreadモード、'u'はupdateモードです。

mrtopdict関数は、データディクショナリーのオープンに成功した場合は、trueを返し、失敗した場合には、falseを返します。 失敗した場合には、mroperr変数にエラーコードがセットされます。

同一のデータベースから、複数のテーブルをオープンする場合には、データディクショナリーをオープンしてから、それから データベース名の代わりに、CHARNILを指定して、mropen関数またはmrtopen関数を呼び出すと効率が良くなります。 データディクショナリーをオープンし、データベース内の複数テーブルをオープンし、データディクショナリーをクローズする 方法は、単純にテーブルをオープンして、その度にデータディクショナリーにアクセスする方法より、処理時間を短縮できます。 データディクショナリーをオープンしておけば、mropen関数、mrtopen関数を呼び出す度に、アクセス特権やロックレベルを 瞬時に取得できます。

mropen関数とmrtopen関数のために、省略値が明確であることを必要とするため、データディクショナリーのオープンは 、1つのデータベースに限ることに注意してください。

例

以下に示す、opdict.cはデータベースのデータディクショナリーをオープンし、loansテーブルをオープンして、次に データディクショナリーをクローズしてから、loansテーブルをクローズします。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr     loans_tabdesc;

            mropdict (DATABASE, 'r');
            loans_tabdesc = mropen (CHARNIL, "loans", 'u');
            mrcldict ();
            mrclose (loans_tabdesc);
       }

## 3.4 データベース名のチェック

mrqdb関数は、ディレクトリがEmpressデータベースであるかチェックします。引数にディレクトリを指定し、データベースで ある場合にはtrueを返し、そうでない場合にはfalseを返します。一般的な形式を以下に示します。

       flag = mrqdb (directory);

次の章のサンプルプログラム、locktab.cにデータベースチェックを解説しています。

## 3.5 ロック

ロックは、テーブルレベル、グループレベル、レコードレベルでサポートされます。テーブルがオープンされると、データ ディクショナリーで設定されている、ロックレベルが取得されます。テーブルレベルのロックはmropen関数で、 グループレベルのロックはmrgetbegin関数で、レコードレベルのロックはmrget関数で実行されます。

テーブルレベルロックは、mrlktab関数で実行することも可能です。この関数へは、テーブル記述子が引数として渡されます。 ここで行なわれるロックは、mropen関数によって行なわれたロックの追加になります。データディクショナリーで、テーブル に対してロックが設定されていない場合には、mropen関数またはmrlktab関数によるロックはできません。

melkrec関数は、検索したレコードに、レコードレベルロックを行います。またmrulrec関数は、このロックを解除します。 レコードへの操作が完了したら、レコードを解除する必要があります。レコードをロックしたままにしておくと、他のプログラム によるアクセスが失敗してしまいます。

mrlktab関数もmrlkrec関数も、成功した場合はtrueを、失敗した場合はfalseを返します。一般的な形式を以下に示します。

       flag = mrlktab (table_desc);

       flag = mrlkrec (record_desc);

       mrultab (table_desc);

       mrulrec (record_desc);

データディクショナリーで、テーブルにロックが設定されていない場合には、melktab関数とmrlkrec関数はtrueを返しますが 、ロックは実行されません。テーブルのロックレベルは、sys_tablesテーブル中の、tab_lockフィールドで設定され、ロックが 有効であるかチェックされます。

例

以下に示すプログラム、locktab.cはディレクトリが、Empressデータベースであるかチェックし、データベースである場合には テーブルをオープンし、オープン時にセットした、テーブルレベルのロックを解除します。（テーブルレベルロックがデータ ディクショナリーで、テーブルに対して設定されていると仮定した場合） その後、明示的にテーブルレベルのロックを設定し、テーブルのクローズ前にロックを解除します。

       #include   <mscc.h>

       #define    DATABASE   "repairs"

       msmain ()
       {
            addr   loans_tabdesc;

            if (! mrqdb (DATABASE))
            {
                 fprintf (stderr, "%s is not an Empress\
                      database", DATABASE); msexit (1);
            }
            loans_tabdesc = mropen (DATABASE, "loans", 'u');

            mrultab (loans_tabdesc);

            if (! mrlktab (loans_tabdesc))
            {
                 fprintf (stderr, "Unable to lock table.\
                      Try again later."); msexit (1);
            }
            mrultab (loans_tabdesc);

            mrclose (loans_tabdesc);
       }

## 3.6 フィールド記述子の取得

フィールド値を使用する場合、または新しいデータをフィールドに割当てる場合には、フィールド記述子を取得する必要が あります。フィールド記述子を取得する関数には、mrigeta関数とmrngeta関数があります。これらの関数は、引数として テーブル記述子とフィールド番号またはフィールド名を指定します。mrigeta関数は、フィールド番号が有効でない場合には、 NULLポインタを返します。また、mrngeta関数もフィールド名が有効でない場合には、NULLポインタを返します。これらの 一般的な形式を以下に示します。

       attr_desc = mrigeta (table_desc, attr_number);

       attr_desc = mrngeta (table_desc, attr_name);

例

以下に示すプログラム、ngeta.cはmrngeta関数を使用して、personnelテーブル内の4つのフィールドに対して、フィールド 記述子を取得します。

       #include   <mscc.h>

       #define    DATABASE   "repairs"

       msmain ()
       {
            addr   pers_tabdesc, number_attrdesc,
                   name_attrdesc, phone_attrdesc,
                   credit_attrdesc;

            pers_tabdesc = mropen (DATABASE, "personnel", 'r');

            number_attrdesc = mrngeta (pers_tabdesc, "number");
            name_attrdesc = mrngeta (pers_tabdesc, "name");
            phone_attrdesc = mrngeta (pers_tabdesc, "phone");
            credit_attrdesc = mrngeta (pers_tabdesc, "credit_limit");

            mrclose (pers_tabdesc);
       }

例

以下に示すプログラム、igeta.cはmrigeta関数を使用して、loansテーブルのすべてのフィールドに対して、フィールド記述子 を取得します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr    loans_tabdesc, attr_desc[100];
            int     i;

            loans_tabdesc = mropen (DATABASE, "loans", 'r');

            /* attr_desc[0] is not used, so that each subscript of attr_desc
               will correspond to the number of the attribute it refers to, we
               assume there are fewer than 99 attributes                        */

            for (i = 1 ; (attr_desc[i] = mrigeta (loans_tabdesc, i)) != 0 ; i++);
                 /* loop has no body */

            mrclose (loans_tabdesc);
       }

## 3.7 フィールド名の検索

フィールドの番号だけが判明していて、フィールド名が判明していない場合は、mrganame関数を使用して、フィールド名を 取得できます。引数としてフィールド記述子を渡し、フィールド名へのポインタを返します。一般的な形式を以下に示します。

       name = mrganame (attr_desc);

例

以下に示すプログラム、getname.cはloansテーブル内のすべてのフィールド名を表示します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr    loans_tabdesc, attr_desc;
            int     i;
            char*   name;

            loans_tabdesc = mropen (DATABASE, "loans", 'r');

            for (i = 1 ; (attr_desc = mrigeta (loans_tabdesc, i)) != 0 ; i++)
            {
                 name = mrganame (attr_desc);
                 printf("%s\n",name);
            }
            mrclose (loans_tabdesc);
       }

## 3.8 レコード記述子の取得

レコードを検索または、追加する前に個々のレコードを保存するスペースを割当てる必要があります。mrmkrec関数が この処理を実行します。この関数は、テーブル記述子を引数とし、レコード記述子を返します。

レコード記述子が必要でなくなった場合には、割当てられたスペースは、mrfrrec関数を使用して解放する必要があります。 この関数は、レコード記述子を引数とし、値は返しません。一般的な形式を以下に示します。

       record_desc = mrmkrec (table_desc);

       mrfrrec (record_desc);

例

以下に示すプログラム、record.cはレコード記述子を作成し、その後に割当てられたスペースを解放します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr   loans_tabdesc, loans_recdesc;

            loans_tabdesc = mropen (DATABASE, "loans", 'u');
            loans_recdesc = mrmkrec (loans_tabdesc);
            mrfrrec (loans_recdesc);
            mrclose (loans_tabdesc);
       }

## 3.9 レコードへのフィールド値の入力

外部フォーマットのフィールド値（文字列）は、mrputvs関数を使用して、一度に一つずつレコードに割当てられます。この 関数は、引数としてレコード記述子、フィールド記述子、文字列を受け渡します。

フィールドに割当てられる値が、Cのinteger値である場合は、integer値を文字列に変換する必要はなく、mrputvs関数の 代わりに、mrputvi関数を使用します。これらの関数は、成功した場合はtrueを返し、失敗したらfalseを返します。

外部フォーマットフィールド値よりも、内部フォーマットフィールド値のポインタを使用するほうが望ましい場合は、mrputi関数 を使用してください。ただし、この関数を使用するには、アプリケーションが実行されているマシン上で、各Empressの データタイプが、どのように表現されるか正確に知っておく必要があります。またこれらの関数を使用したプログラムは 、機種間で移植性が悪くなります。一般的な形式を以下に示します。

       flag = mrputvs (record_desc, attr_desc, value);

       flag = mrputvi (record_desc, attr_desc, integer);

       flag = mrputi (record_desc, attr_desc, var_ptr);

       mrmptvs (record_desc, attr_desc, value);

       mrmptvi (record_desc, attr_desc, integer);

`var_ptr` は、内部フォーマット値を含む変数へのポインタです。

レコードは、mradd関数またはmrtadd関数が呼び出されるまで、実際にテーブル内には挿入されないことに注意してください。

例

以下に示すプログラム、putval.cはloansテーブルの新しいレコードに、フィールド値を入力します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr   loans_tabdesc, loans_recdesc,
                   name_attrdesc, date_attrdesc,
                   amount_attrdesc;

            loans_tabdesc = mropen (DATABASE, "loans", 'u');

            name_attrdesc = mrngeta (loans_tabdesc, "name");
            date_attrdesc = mrngeta (loans_tabdesc, "date");
            amount_attrdesc = mrngeta (loans_tabdesc, "amount");
            loans_recdesc = mrmkrec (loans_tabdesc);

            if (! mrputvs (loans_recdesc, name_attrdesc, "Jones"))
            {
                 fprintf (stderr, "Name conversion unsuccessful\n");
                 msexit (1);
            }
            else if (! mrputvs (loans_recdesc, date_attrdesc, "2 July 1992"))
            {
                 fprintf (stderr, "Date conversion unsuccessful\n");
                 msexit (1);
            }
            else if (! mrputvs (loans_recdesc, amount_attrdesc, "$75.00"))
            {
                 fprintf (stderr, "Amount conversion unsuccessful\n");
                 msexit (1);
            }
            else printf ("Conversion successful\n");

            mrfrrec (loans_recdesc);

            mrclose (loans_tabdesc);
       }

レコード生成時には、レコード内のすべてのフィールド値は、NULLに設定されますので、個々に値を割当てるころが必要 になります。mrsetnv関数は、フィールド値をNULLに設定します。この関数は、引数としてレコード記述子とフィールド記述子 を渡します。レコード全体は、mrsetnr関数を使用してNULLに設定され、この関数はレコード記述子を引数とします。一般的 な形式を以下に示します。

       mrsetnv (record_desc, attr_desc);

       mrsetnr (record_desc);

例

以下に示すプログラム、null.cはloansテーブルのレコードにある、dateフィールドをNULLに設定し、その後レコード全体を NULLに設定します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr     loans_tabdesc, first_recdesc,
                     second_recdesc, name_attrdesc;
            addr     date_attrdesc, amount_attrdesc;

            loans_tabdesc = mropen (DATABASE, "loans", 'u');

            name_attrdesc = mrngeta (loans_tabdesc, "name");
            date_attrdesc = mrngeta (loans_tabdesc, "date");
            amount_attrdesc = mrngeta (loans_tabdesc, "amount");

            first_recdesc = mrmkrec (loans_tabdesc);

            if (! mrputvs (first_recdesc, name_attrdesc, "Mosca"))
            {
                 fprintf (stderr, "Name conversion unsuccessful\n");
                 msexit (1);
            }
            else if (! mrputvs (first_recdesc, amount_attrdesc, "$75.00"))
            {
                 fprintf (stderr, "Amount conversion unsuccessful\n");
                 msexit (1);
            }
            else printf ("Conversion successful\n");

            mrsetnv (first_recdesc, date_attrdesc);

            second_recdesc = mrmkrec (loans_tabdesc);

            mrsetnr (second_recdesc);

            mrfrrec (first_recdesc);
            mrfrrec (second_recdesc);

            mrclose (loans_tabdesc);
       }

## 3.10 テーブルへのレコード追加

レコードにフィールド値が割当てられている場合、そのレコードはテーブルに追加することができます。これは mradd関数またはmrtadd関数によって実行され、これらの関数は、引数としてレコード記述子を使用して、テーブルに レコードを挿入します。

`mradd`関数は値を返さず、失敗した場合には、呼出しプログラムを終了させます。レコードが挿入できない可能性が ある場合（ユニークなインデックスが設定されているのに、重複値を入力したり、ロックがかけられる場合）、mradd関数ではなく mrtadd関数を使用してください。mrtadd関数は、成功した場合はtrueを、失敗した場合はfalseを返します。

テーブルへの挿入が終了したばあい、mraddend関数を呼び出してください。 この関数は引数にレコード記述子を渡し、Empressの内部バッファから、ファイルに書き込まれます。一般的な形式を以下に示します。

       mradd (record_desc);

       flag = mrtadd (record_desc);

       mraddend (record_desc);

例

以下に示すプログラム、add.cはloansテーブル内に前述の例で生成された、レコードを1つ挿入します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr   loans_tabdesc, loans_recdesc,
                   name_attrdesc, date_attrdesc,
                   amount_attrdesc;

            loans_tabdesc = mropen (DATABASE, "loans", 'u');

            name_attrdesc = mrngeta (loans_tabdesc, "name");
            date_attrdesc = mrngeta (loans_tabdesc, "date");
            amount_attrdesc = mrngeta (loans_tabdesc, "amount");

            loans_recdesc = mrmkrec (loans_tabdesc);

            if (! mrputvs (loans_recdesc, name_attrdesc, "Jones"))
            {
                 fprintf (stderr, "Name conversion unsuccessful\n");
                 msexit (1);
            }
            else if (! mrputvs (loans_recdesc, date_attrdesc,
                     "2 July 1992"))
            {
                 fprintf (stderr, "Date conversion unsuccessful\n");
                 msexit (1);
            }
            else if (! mrputvs (loans_recdesc,
                 amount_attrdesc, "$75.00"))
            {
                 fprintf (stderr, "Amount conversion unsuccessful\n");
                 msexit (1);
            }

            mradd (loans_recdesc);
            mraddend (loans_recdesc);

            mrfrrec (loans_recdesc);

            mrclose (loans_tabdesc);
       }

## 3.11 レコードの検索

テーブルからレコードを検索するには、以下の6グループのルーチンが使用されます。

1. mrgetbegin関数、mrtgtbegin関数、mrsrtbegin関数、mrtsrbegin関数は、検索プロシージャを初期化し、 検索条件とテーブルのレコードを関連付けます。

2. mrget関数、mrprev関数、mrtget関数、mrtprev関数、mrreget関数、mrreprev関数、mrgetrec関数は レコードをフェッチします。

3. mrspv関数は、フィールド値の保存用にスペースを割当てます。

4. mrgetvs関数、nrgetvi関数、mrcopyv関数、mrcopyi関数、mrgeti関数はフィールド値を検索します。 さらにmrgfunc関数とmrtgfunc関数は、集計関数の結果を返します。

5. mrgetptr関数は、後に使用するレコードへのポインタを取得します。

6. mrgetend関数とmrfree関数は、検索後のクリーンアップをします。

### 3.11.1 検索の初期化

mrgetbegin関数は、1つ以上のレコードと検索条件を関連付け、レコードの検索処理における初期化を行います。検索条件 は、クエリーのWHERE句に類似しています。グループレベルロックがテーブルに設定されている場合は、この関数はレコードのグループをロックします。

mrgetbegin関数は、引数として検索条件記述子と、1つ以上のレコード記述子を受け渡します。（mscc.hで定義されている ADDRNILによって引数の終了を宣言します。）複数のレコード記述子が存在する場合、各々別々のテーブルにより生成 されていなくてはなりません。これによりジョイン（テーブルの結合）が可能となります。2つのテーブルをジョインする場合 には、2つのレコード記述子が必要であり、2つのテーブルをオープンしなくてはなりません。mrgetbegin関数は、mrget関数 で必要とされる、検索記述子を返し、何らかの理由で必要なレコードにアクセスできない場合には、呼出しプログラムを 終了します。またこの関数は、検索条件がない場合でも呼び出さなくてはなりません。この場合には、第一引数として ADDRNILを指定します。

mrtgtbegin関数は、失敗した場合にADDRNILを返すこと以外は、mrgetbegin関数と同じです。

ソートされた順番で、検索レコードを取得したい場合には、mrsrtbegin関数またはmrtsrbegin関数を使用します。これらの 関数は、引数として検索条件記述子、ソート形態を表す文字（'s'または'u'）、1つ以上のレコード記述子、終了宣言の ADDRNIL、ソートに使用するフィールド記述子と、ソート順序の昇順または降順を表す文字（'a'または'd'）をセットで 1つ以上指定し、最後にADDRNILを指定します。ユニークなソートは、引数に指定されたフィールドについてユニークが 保証されます。引数に指定されていないフィールドについては、重複値を含む場合は検索の順番は不定となります。

これらの関数は、検索記述子を返します。mrsrtbegin関数は、すべての必要なレコードにアクセスできない場合には、 呼出しプログラムを終了し、mrtsrbegin関数は、ADDRNILを返します。

一般的な形式を以下に示します。

       retrieval_desc = mrgetbegin (qual_desc,
            record_desc_1, record_desc_2, .... , ADDRNIL);

       retrieval_desc = mrtgtbegin (qual_desc,
            record_desc_1, record_desc_2, .... , ADDRNIL);

       retrieval_desc = mrsrtbegin (qual_desc, indicator,
            record_desc_1, record_desc_2, .... ,
            ADDRNIL, attr_desc_1, type, attr_desc_2,
            type, .... , ADDRNIL);

       retrieval_desc = mrtsrbegin (qual_desc, indicator,
            record_desc_1, record_desc_2, .... ,
            ADDRNIL, attr_desc_1, type, attr_desc_2,
            type, .... , ADDRNIL);

### 3.11.2 レコードの取得

レコード取得用の関数は、引数として検索記述子を指定します。mrgetrec関数の場合には、レコード記述子と以前に 取得したレコードへのポインタを指定します。テーブルに対してレコードレベルロックが設定されている場合には、これらの 関数の実行時にレコードをロックします。

mrget関数は、最も単純な関数であり、検索条件にあったレコードを取得します。検索に成功した場合はtrueを、失敗した 場合にはfalseを返します。レコードにアクセスできない場合（ロックされていたなど）、または表現式を評価する際に問題 が発生した場合（0による除算など）には、エラーメッセージを出力して呼出しプログラムを終了します。

mrtget関数は、失敗した場合には-1を返すことを除けば、レコードの末尾に到達した場合には0を、成功した場合には1を 返すことはmrget関数と同一です。

mrget関数とmrtget関数の、継続呼出しは、すべての検索条件に一致したレコードを順次、読込みます。mrreget関数は 、新しいレコードを読み込まず、mrget関数で既に読み込んだ、最終レコードを再度読込みます。これは通常、ロック されたレコードに対して、再度読込むために使用されます。

mrprev関数、mrtprev関数、mrreprev関数は、前のレコードを読込む場合に使用します。

mrgetrec関数は、引数にmrgetptr関数で取得したレコードへのポインタを指定し、単一のレコードを読込みます。 成功した場合にはtrueを、失敗した場合にはfalseを、ロックされているばあいには-1を返します。

これらの関数の一般的な形式を以下に示します。

       flag = mrget (retrieval_desc);

       flag = mrtget (retrieval_desc);

       flag = mrreget (retrieval_desc);

       flag = mrprev (retrieval_desc);

       flag = mrtprev (retrieval_desc);

       flag = mrreprev (retrieval_desc);

       flag = mrgetrec (record_desc, record_ptr);

### 3.11.3 フィールド値のスペース割付け

mrspv関数は検索されたフィールド値を、外部フォーマットで保存するためのスペースを割当てます。引数にフィールド 記述子を指定し、外部フォーマットフィールド値の、保存スペースへのポインタを返します。このポインタは通常、 mrcopyv関数に渡されます。割当てられたスペースは、mrfree関数によって解放します。この関数は、TEXTまたはBULK データ値用に、スペースを割当てることはできません。もし、これらのデータタイプのフィールド記述子が渡された場合には、 NULLポインタを返します。TEXTとBULKデータ値の検索については、次の章で解説します。

この関数の一般的な形式を示します。

       space = mrspv (attr_desc);

### 3.11.4 フィールド値の検索

#### 3.11.4.1 外部表現

mrcopyv関数は、フィールド値を検索する最も簡単な関数であり、外部フォーマット値を、mrspv関数によって割当てた スペースに保存します。この関数は、引数にレコード記述子、フィールド記述子、mrspv関数によって割当てられた スペースへのポインタを指定します。成功した場合はtrueを、失敗した場合はfalseを返します。

検索されるフィールドのデータタイプがTEXTである場合、mrcopyv関数の代わりに、mrgetvs関数を使用しなくては なりません。（mrgetvs関数は、すべてのフィールド値を検索する場合に使用できます。）これは、可変長データ用の スペース割当て処理が難しいためです。mrgetvs関数を使用する場合は、この関数がスペース割当てを行なうので mrspv関数を使用する必要はありません。mrgetvs関数は、引数にレコード記述子とフィールド記述子を指定し、 文字列へのポインタを返します。文字列が保存されているバッファは、次のmrgetvs関数の呼出しで、再使用されます。 よって文字列が必要な場合には、コピーしておく必要があります。

検索値をinteger値に変換するような場合、integer値を直接返すmrgetvi関数を使用してください。（Empressは内部で mrgetvs関数を使用して、値をinteger値に変換して返しています。）mrgetvi関数は、変換に失敗した場合、呼出し プログラムを終了します。

これらの一般的な形式を以下に示します。

       flag = mrcopyv (record_desc, attr_desc, space);

       result = mrgetvs (record_desc, attr_desc);

       result = mrgetvi (record_desc, attr_desc);

#### 3.11.4.2 内部表現

処理時間を短縮するために、内部フォーマットフィールド値へのポインタを使用したい場合、mrcopyv関数の代わりに mrcopyii関数を使用してください。この関数も、引数にレコード記述子、フィールド記述子、値の保存スペースへのポインタを 指定します。この保存スペースは、一致したデータタイプとサイズであることが必要です。この関数を使用するには、 アプリケーションを実行するマシンで、Empressデータタイプがどのように表現されるか、性格に知っておく必要があります。 さらに、このプログラムは機種間で移植性が悪くなります。

mrcopyi関数は、検索に成功した場合のtrueを、失敗した場合にはfalseを返します。

mrgeti関数も、内部フォーマットフィールド値を検索します。引数として、レコード記述子、フィールド記述子を指定し、 値へのポインタを返します。（値がNULLの場合は、NULLポインタを返します。）この関数は、基本的にBULKデータ値 に対して使用することを意図して、提供されています。mrgetvs関数を使用する場合のように、返される値は、次回の呼出し で再使用される、バッファに保存されます。必要により値をコピーしておかなくてはなりません。

これらの一般的な形式を以下に示します。

       flag = mrcopyi (record_desc, attr_desc, var_ptr);

       result = mrgeti (record_desc, attr_desc);

### 3.11.5 検索後のクリーンアップ

すべての検索が終了したら、mrgetend関数を呼び出す必要があります。この関数は、引数に検索記述子を指定し、 検索記述子に割当てられたスペースを解放します。一般的な形式を以下に示します。

       mrgetend (retrieval_desc);

### 3.11.6 集計関数-COUNT、MAX、MIN、AVG、SUM

mrgfunc関数とmrtgfunc関数は、検索レコードのカウント、指定フィールドの最大値、最小値、平均値、合計値を取得する ために使用されます。これらの関数は、引数に集計関数名（COUNT、MAX、MIN、AVG、SUM）、検索記述子、レコード記述子、 フィールド記述子を指定します。COUNTの場合には、フィールド記述子にはADDRNILを指定します。

どちらの関数も、関数値へのポインタを返します。mrgfunc関数は、失敗した場合に呼び出しプログラムを終了させますが、 mrtgfunc関数は、失敗した場合にはCHARNILを返します。

これらの一般的な形式を以下に示します。

       value = mrgfunc (function, retrieval_desc, record_desc, attr_desc);

       value = mrtgfunc (function, retrieval_desc, record_desc, attr_desc);

mrgfunc関数、mrtgfunc関数を呼び出した後には、mrgetend関数を呼び出さなくてはなりません。文字列は割当てられた スペースに保存されますので、必要がなくなった場合には、mrfree関数で解放してください。

### 3.11.7 サンプル

personnelテーブルの、全従業員の名前と電話番号を検索し表示するとき、検索条件のない単純な検索では、名前と電話番号 を保存するスペースを割当てるため、mrspv関数を呼び出し、mrgetbegin関数で検索を開始し、mrget関数で各レコードを 取得するループを実行します。このループ内で、mrcopyv関数が各フィールドに対して呼び出され、値を取得します。 ループが終了した時点で、mrgetend関数を呼び出します。 以下に示す、employee.cは、上記のプログラム例です。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr     pers_tabdesc, name_attrdesc,
                     phone_attrdesc, pers_recdesc;
            addr     retrieve_desc;
            int      employees;
            char*    name_value;
            char*    phone_value;

            pers_tabdesc = mropen (DATABASE, "personnel", 'r');

            pers_recdesc = mrmkrec (pers_tabdesc);
            name_attrdesc = mrngeta (pers_tabdesc, "name");
            phone_attrdesc = mrngeta (pers_tabdesc, "phone");

            name_value = mrspv (name_attrdesc);
            phone_value = mrspv (phone_attrdesc);

            printf ("Current Employees\n\n");
            printf (" Name Phone\n");

            employees = 0;
            retrieve_desc = mrgetbegin (ADDRNIL, pers_recdesc, ADDRNIL);
            while (mrget (retrieve_desc))
            {
                 mrcopyv (pers_recdesc, name_attrdesc, name_value);
                 mrcopyv (pers_recdesc, phone_attrdesc, phone_value);

                 printf ("%-12s%-12s\n", name_value, phone_value);
                 employees++;
            }
            mrgetend (retrieve_desc);

            printf ("\nTotal Number of Employees = %d\n", employees);

            mrfree (name_value);
            mrfree (phone_value);
            mrfrrec (pers_recdesc);
            mrclose (pers_tabdesc);
       }

以下に示す、sort_emp.cはmrsrtbegin関数を使用して、降順にソートした従業員リストを表示します。

       #include    <msccc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            int     employees;
            addr    pers_tabdesc, name_attrdesc, phone_attrdesc,
                    pers_recdesc;
            addr    retrieve_desc;
            char*   name_value;
            char*   phone_value;

            pers_tabdesc = mropen (DATABASE, "personnel", 'r');
            pers_recdesc = mrmkrec (pers_tabdesc);
            name_attrdesc = mrngeta (pers_tabdesc, "name");
            phone_attrdesc = mrngeta (pers_tabdesc, "phone");

            name_value = mrspv (name_attrdesc);
            phone_value = mrspv (phone_attrdesc);

            printf ("Current Employees\n\n");
            printf (" Name Phone\n");

            employees = 0;
            retrieve_desc = mrsrtbegin (ADDRNIL, 's',
                 pers_recdesc, ADDRNIL, name_attrdesc, 'd', ADDRNIL);

            while (mrget (retrieve_desc))
            {
                 mrcopyv (pers_recdesc, name_attrdesc, name_value);
                 mrcopyv (pers_recdesc, phone_attrdesc,phone_value);
                 printf ("%-12s%-12s\n", name_value, phone_value);
                 employees++;
            }
            mrgetend (retrieve_desc);
            printf ("\nTotal Number of Employees = %d\n", employees);

            mrfree (name_value);
            mrfree (phone_value);
            mrfrrec (pers_recdesc);
            mrclose (pers_tabdesc);
       }

## 3.12 比較

mrcompare関数、mrnullr関数、mrnullv関数を使用して、検索値に対して比較を行なうことができます。

mrcompare関数は、フィールド値をファイルフォーマットの定数と比較します。この関数は引数にレコード記述子、 フィールド記述子、ファイルフォーマット値を含む変数へのポインタを使用します。一般的な形式を以下に示します。

       result = mrcompare (record_desc, attr_desc, var_ptr);

ファイルフォーマットへのポインタは、mrcvt関数を使用して、以下のように指定できます。（詳細は次の章で解説します。）

       result = mrcompare (record_desc, attr_desc, mrcvt (attr_desc, string));

mrnullr関数とmrnullv関数は、NULL値との比較に使用されます。レコード全体がNULL値かチェックするにはmrnullr関数を 使用します。この関数は、引数にレコード記述子を指定し、レコードがNULLならtrueを返し、NULLでない場合にはfalseを 返します。mrnullv関数が、フィールド値がNULLであるかチェックします。この関数は、引数にレコード記述子とフィールド 記述子を指定し、フィールド値がNULLならtrueを、NULLでない場合はfalseを返します。これらの一般的な形式を示します。

       flag = mrnullr (record_desc);

       flag = mrnullv (record_desc, attr_desc);

### 3.12.1 ファイルフォーマットへの変換

mrcvt関数、mrcvtv関数、mrcvtv2関数は、外部フォーマットフィールド値を、ファイルフォーマットに変換し、mrcvtin関数 、mrcvtu関数、mrcvti2関数は、内部フォーマット値をファイルフォーマット値へ変換します。これらの関数は、ファイル フォーマット値へのポインタを返し、変換に失敗した場合は、ADDRNILを返します。

mrcvt関数とmrcvtin関数は、通常mrqcon関数またはmrtqcon関数の引数に指定されます。これらの関数は、他の多くの mrルーチン（mrgetvs関数、mrgetvi関数など）でも使用される、変換用のバッファを共有するため、変換した値は永続的に 保存されません。

これらの関数で使用されるバッファは、個々の呼出しで再使用されるため、mrqrng関数、またはmrtqrng関数では使用 できません。もし使用した場合は、範囲の上限への呼出しが、下限用に変換された値を破壊します。mrcvt関数と mrcvt2関数（あるいは、mrcvti関数とmrcvti2関数）の2つの関数を、代わりに使用してください。mrcvtv関数とmrcvti関数は 、互いにバッファを共有し、mrcvtv2関数とmrcvti2関数は、別のバッファを共有します。

これらの一般的な形式を以下に示します。

       value = mrcvt (attr_desc, string);

       value = mrcvtv (attr_desc, string);

       value = mrcvtv2 (attr_desc, string);

       value = mrcvtin (attr_desc, var_ptr);

       value = mrcvti (attr_desc, var_ptr);

       value = mrcvti2 (attr_desc, var_ptr);

stringは外部フォーマットフィールド値です。var_ptrは、内部フォーマットフィールド値を含む変数へのポインタです。

## 3.13 検索条件

検索条件は、クエリーのWHERE句に類似しています。mrq...関数の呼出しによって生成されます。これらの関数は mrgetbegin関数グループの1つに受け渡される、検索条件記述子を返します。

検索条件の構築には、16の関数が使用されます。それらはmrqcon関数、mrtqcon関数、mrqrng関数、mrtqrng関数、 mrqatr関数、mrtqatr関数、mrqmch関数、mrtqmch関数、mrqnul関数、mrtqnul関数、mrqseq関数、mrqieq関数、 mrqand関数、mrqor関数、mrqnot関数、mrqlst関数です。

### 3.13.1 定数との比較

mrqcon関数とmrtqcon関数は、フィールド値と定数を比較し、引数に、">"、"<"、">="、"<>"、"<="、"!="（"~"は"!"と 同様に指定できます。)の6つの演算子のうちどれか1つ、フィールド記述子、ファイルフォーマットの定数値を指定します。 mrqcon関数は、引数に問題がある場合、呼出しプログラムを終了しますが、mrtqcon関数は、その場合にはADDRNILを 返します。これらの一般的な形式を以下に示します。

       qual_desc = mrqcon (operator, attr_desc, var_ptr);

       qual_desc = mrtqcon (operator, attr_desc, var_ptr);

mrqcon関数とmrtqcon関数による、変換関数の使用は、前の章の外部フォーマットまたは内部フォーマットから、ファイル フォーマットへの変換で解説しています。

例

以下に示すプログラム、constant.cはmrqcon関数を使用して、ローンの額が$100より大きいレコードを、loansテーブルから 検索します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr   loans_tabdesc, name_attrdesc,
                   date_attrdesc;
            addr   amount_attrdesc, loans_recdesc,
                   qual, retrieve_desc;
            char*  name_value;
            char*  date_value;
            char*  amount_value;

            loans_tabdesc = mropen (DATABASE, "loans", 'r');

            loans_recdesc = mrmkrec (loans_tabdesc);
            name_attrdesc = mrngeta (loans_tabdesc, "name");
            date_attrdesc = mrngeta (loans_tabdesc, "date");
            amount_attrdesc = mrngeta (loans_tabdesc, "amount");

            name_value = mrspv (name_attrdesc);
            date_value = mrspv (date_attrdesc);
            amount_value = mrspv (amount_attrdesc);

            printf ("Employees With Single Loans Of More Than $100\n\n");
            printf (" Name     Date     Amount\n");

            qual = mrqcon (">", amount_attrdesc, mrcvt (amount_attrdesc, "$100.00"));

            retrieve_desc = mrgetbegin (qual, loans_recdesc, ADDRNIL);
            while (mrget (retrieve_desc))
            {
                 mrcopyv (loans_recdesc, name_attrdesc, name_value);
                 mrcopyv (loans_recdesc, date_attrdesc, date_value);
                 mrcopyv (loans_recdesc, amount_attrdesc,amount_value);
                 printf ("%-12s%-22s%-12s\n", name_value, date_value, amount_value);
            }
            mrgetend (retrieve_desc);

            mrfree (name_value);
            mrfree (date_value);
            mrfree (amount_value);
            mrfrrec (loans_recdesc);

            mrclose (loans_tabdesc);
       }

### 3.13.2 範囲との比較

mrqrng関数とmrtqrng関数は、フィールド値を値の範囲と比較します。これらの関数は、引数にフィールド記述子、ファイル フォーマットの範囲の下限値、下限値を含むか否かの指定文字'i'または'e'、ファイルフォーマットでの上限値、上限値を 含むか否かの指定文字'i'または'e'を指定します。mrqrng関数は、引数について問題が発生した場合には、呼出し プログラムを終了します。mrtqrng関数は、同様の場合にはADDRNILを返します。一般的な形式を以下に示します。

       qual_desc = mrqrng (attr_desc, lower_limit,
            limit_type_1, upper_limit, limit_type_2);

       qual_desc = mrtqrng (attr_desc, lower_limit,
            limit_type_1, upper_limit, limit_type_2);

mrqrng関数、mrtqrng関数で必要な、外部フォーマットまたは内部フォーマットフィールド値の、ファイルフォーマットへの 変換は、変換関数を使用して行います。

例

以下に示すプログラム、range.cはmrqrng関数を使用して、ローンの額が$100より大きく、$200以下のレコードを検索します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr   loans_tabdesc, name_attrdesc,
                   date_attrdesc;
            addr   amount_attrdesc, loans_recdesc,
                   qual, retrieve_desc;
            char*  name_value;
            char*  date_value;
            char*  amount_value;

            loans_tabdesc = mropen (DATABASE, "loans", 'r');

            loans_recdesc = mrmkrec (loans_tabdesc);
            name_attrdesc = mrngeta (loans_tabdesc, "name");
            date_attrdesc = mrngeta (loans_tabdesc, "date");
            amount_attrdesc = mrngeta (loans_tabdesc, "amount");

            name_value = mrspv (name_attrdesc);
            date_value = mrspv (date_attrdesc);
            amount_value = mrspv (amount_attrdesc);

            printf ("Employees Owing Between $100 and $200\n\n");
            printf (" Name          Date           Amount\n");
            qual = mrqrng (amount_attrdesc, mrcvtv
                   (amount_attrdesc, "$100.00"), 'e', mrcvtv2
                   (amount_attrdesc, "$200.00"), 'i');

            retrieve_desc = mrgetbegin (qual, loans_recdesc, ADDRNIL);
            while (mrget (retrieve_desc))
            {
                 mrcopyv (loans_recdesc, name_attrdesc, name_value);
                 mrcopyv (loans_recdesc, date_attrdesc, date_value);
                 mrcopyv (loans_recdesc, amount_attrdesc, amount_value);
                 printf ("%-12s%-22s%-12s\n", name_value, date_value, amount_value);
            }
            mrgetend (retrieve_desc);

            mrfree (name_value);
            mrfree (date_value);
            mrfree (amount_value);
            mrfrrec (loans_recdesc);

            mrclose (loans_tabdesc);
       }

### 3.13.3 他のフィールドとの比較

mrqatr関数とmrtqatr関数は、2つのフィールド値を比較します。これらの関数は、引数に6つの演算子のうちの1つと、 2つのフィールド識別子を指定します。mrqatr関数は、引数に問題がある場合には、呼出しプログラムを終了しますが 、mrtqatr関数は、この場合ADDRNILを返します。これらの一般的な形式を以下に示します。

       qual_desc = mrqatr (operator, attr_desc_1, attr_desc_2);

       qual_desc = mrtqatr (operator, attr_desc_1, attr_desc_2);

例

以下に示すプログラム、attrcmp.cはmrqatr関数を使用して、個々の従業員のローンを合計し、その合計が従業員の クレジット限度額を超えているものを表示します。（シェルインタフェース、または標準Cインタフェースが使用された場合、 テンポラリファイルを使用しないと実行できないので注意してください。）dollcvt関数は、DOLLAR値をFLOATまたは DOUBLEの適切なデータタイプに変換します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       extern      double     dollcvt ();

       msmain ()
       {
            addr      loans_tabdesc, pers_tabdesc,
                      loans_recdesc, pers_recdesc;
            addr      pname_attrdesc, credit_attrdesc,
                      lname_attrdesc, amount_attrdesc;
            addr      qual, p_retrieve_desc,
                      l_retrieve_desc;
            double    amount, sum, limit;
            char*     pname_value;
            char*     credit_value;
            char*     amount_value;

            loans_tabdesc = mropen (DATABASE, "loans",'r');
            pers_tabdesc = mropen (DATABASE, "personnel", 'r');

            loans_recdesc = mrmkrec (loans_tabdesc);
            pers_recdesc = mrmkrec (pers_tabdesc);

            amount_attrdesc = mrngeta (loans_tabdesc, "amount");
            pname_attrdesc = mrngeta (pers_tabdesc, "name");
            lname_attrdesc = mrngeta (loans_tabdesc, "name");
            credit_attrdesc = mrngeta (pers_tabdesc, "credit_limit");

            amount_value = mrspv (amount_attrdesc);
            pname_value = mrspv (pname_attrdesc);
            credit_value = mrspv (credit_attrdesc);

            printf ("Employees Exceeding Credit Limit\n\n");
            printf ("Name          Credit Limit Total\n\n");

            /* join loans and personnel */

            qual = mrqatr ("=", lname_attrdesc, pname_attrdesc);

            p_retrieve_desc = mrgetbegin (qual, pers_recdesc, loans_recdesc, ADDRNIL);

            while (mrget (p_retrieve_desc))
            {
                 /* get name from personnel */
                 mrcopyv (pers_recdesc, pname_attrdesc, pname_value);
                 mrcopyv (pers_recdesc, credit_attrdesc, credit_value);

                 /* go through loans summing all amounts
                 for that name */

                 qual = mrqcon ("=", lname_attrdesc,
                        mrcvt (lname_attrdesc, pname_value));
                 sum = 0;

                 l_retrieve_desc = mrgetbegin (qual, loans_recdesc, ADDRNIL);
                 while (mrget (l_retrieve_desc))
                 {
                      /* fetch amount from loans, turn
                      it into a float value, and sum it */

                      mrcopyv (loans_recdesc, amount_attrdesc,amount_value);
                      amount = dollcvt (amount_value);
                      sum = sum + amount;
                 }
                 mrgetend (l_retrieve_desc);

                 /* turn credit_limit into a float value
                 and compare it with the summed loans */

                 limit = dollcvt (credit_value);
                 if (sum > limit)
                    printf ("%-12s%-12s $%-6.2f\n\n", pname_value, credit_value, sum);
            }
            mrgetend (p_retrieve_desc);

            mrfree (pname_value);
            mrfree (credit_value);
            mrfree (amount_value);
            mrfrrec (loans_recdesc);
            mrfrrec (pers_recdesc);

            mrclose (loans_tabdesc);
            mrclose (pers_tabdesc);
       }

       double    dollcvt   (char* string)
       {
            extern    double   atof ();
            char      c, buffer [20];
            char*     buf_ptr;

            for (buf_ptr = buffer ; (c = *string++) != '\0' ; )
                 if (c != '$' && c != '*' && c != ' ' && c != ',')
                    *buf_ptr++ = c;

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

### 3.13.4 パターンとの比較

mrqmch関数とmrtqmch関数は、フィールド値が文字列パターンと一致するかチェックします。これらの関数は、引数に MATCH、SMATCH、!MATCH、!SMATCHの4つの演算子のうち1つと、フィールド記述子、外部フォーマットの文字パターン を指定します。

パターンはどんな文字でもかまいません。以下に示す文字は特赦な意味を持ちます。

Table 3-1: パターンマッチ文字

文字 | 概要e | 例
`?` | 指定位置には、任意の単一文字が許可されます。 |
`*` | 指定位置には、任意の文字列が許可されます。 |
`[...]` | 指定位置には、指定文字セットのいずれかを許可します。 | `[abc]`は、a、b、cが許可されます。
`{...}` | 指定位置には、0以上の文字列を許可します。 | `{[a-z]}`は、小文字に英字であれば全て許可します。
`[.-.]` | 指定位置には、指定範囲のいずれかの文字が許可されます。 | `[1-cf-i]`は、a、b、c、f、h、iが許可されます。
`[^...]` | 指定位置には、指定文字セット以外のものを許可します。 | `[^123]` は、a、b、c以外の文字を許可します。
`...|...` | "|"を境にして、どちらか一方の値で許可されます。 | `ab|cd`は、abまたはcdが許可されます。
`...&...` | "&"を境にして、両方の値で許可されます。 | `[a-z] & [^x]`は"x"以外の任意の文字に許可されます。
`\` | "?", "*", "|", "&", "{", "}", "[", "]", and "\"などの特殊文字の前に、バックスラッシュを配置した場合、その文字は特殊文字でなく、その文字自身として許可されます。 |

詳細は、Empress SQLリファレンスを参照してください。

`mrqmch`関数は、引数に問題がある場合には、呼出しプログラムを終了しますが、mrtqmch関数は、同様の場合ADDRNIL を返します。一般的な形式を以下に示します。

       qual_desc = mrqmch (operator, attr_desc, pattern);

       qual_desc = mrtqmch (operator, attr_desc, pattern);

例

以下に示すプログラム、match.cはmrqmch関数を使用して、"961"を含んだ電話番号とその持ち主を表示します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr     pers_tabdesc, name_attrdesc,
                     phone_attrdesc, pers_recdesc;
            addr     qual, retrieve_desc;
            char*    name_value;
            char*    phone_value;

            pers_tabdesc = mropen (DATABASE, "personnel", 'r');

            pers_recdesc = mrmkrec (pers_tabdesc);
            name_attrdesc = mrngeta (pers_tabdesc, "name");
            phone_attrdesc = mrngeta (pers_tabdesc, "phone");

            name_value = mrspv (name_attrdesc);
            phone_value = mrspv (phone_attrdesc);

            printf ("Phone Numbers of Employees in Local Area\n\n");
            printf (" Name Phone\n\n");

            qual = mrqmch ("match", phone_attrdesc, "*961*");

            retrieve_desc = mrgetbegin (qual, pers_recdesc, ADDRNIL);
            while (mrget (retrieve_desc))
            {
                 mrcopyv (pers_recdesc, name_attrdesc, name_value);
                 mrcopyv (pers_recdesc, phone_attrdesc, phone_value);
                 printf ("%-12s%-12s\n", name_value, phone_value);
            }

            mrgetend (retrieve_desc);

            mrfree (name_value);
            mrfree (phone_value);
            mrfrrec (pers_recdesc);

            mrclose (pers_tabdesc);
       }

### 3.13.5 NULL値との比較

mrqnul関数とmrtqnul関数は、フィールド値がNULLかチェックします。これらの関数は、引数に"="または"!="のうち いずれかと、フィールド記述子を指定します。mrqnul関数は、引数に問題がある場合には、呼出しプログラムを終了しますが、 mrtqnul関数は、同様の場合ADDRNILを返します。以下に一般的な形式を示します。

       qual_desc = mrqnul (operator, attr_desc);

       qual_desc = mrtqnul (operator, attr_desc);

例

以下に示すプログラム、nullcmp.cはmrqnul関数を使用して、電話番号が不明な従業員を表示します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr     pers_tabdesc, name_attrdesc,
                     phone_attrdesc, pers_recdesc;
            addr     qual, retrieve_desc;
            char*    name_value;

            pers_tabdesc = mropen (DATABASE, "personnel", 'r');

            pers_recdesc = mrmkrec (pers_tabdesc);
            name_attrdesc = mrngeta (pers_tabdesc, "name");
            phone_attrdesc = mrngeta (pers_tabdesc, "phone");

            name_value = mrspv (name_attrdesc);

            printf ("Employees with Unknown Phone Numbers:\n\n");

            qual = mrqnul ("=", phone_attrdesc);
            retrieve_desc = mrgetbegin (qual, pers_recdesc, ADDRNIL);
            while (mrget (retrieve_desc))
            {
                 mrcopyv (pers_recdesc, name_attrdesc, name_value);
                 printf ("%-12s\n", name_value);
            }
            mrgetend (retrieve_desc);

            mrfree (name_value);
            mrfrrec (pers_recdesc);

            mrclose (pers_tabdesc);
       }

### 3.13.6 検索条件の結合

mrqseq関数は、引数にフィールド識別子と文字列を指定し、フィールド値が文字列と等しいかチェックします。文字列が そのフィールドの適正な値に変換できない場合、関数は失敗し呼出しプログラムを終了します。

mrqieq関数は、引数にフィールド識別子とinteger値を指定し、フィールド値がinteger値と等しいかチェックします。integer値 が、そのフィールドの適正な値に変換できない場合、関数は失敗し呼出しプログラムを終了します。

これらのすべての関数が、検索条件識別子を返し、検索条件識別子はmrgetbegin関数の引数として渡されます。

mrqand関数とmrqor関数は、複雑な検索条件を指定するのに使用します。これらの関数は、クエリーのANDとORに類似して おり、引数にmrq...関数によって返される、2つの検索条件識別子を指定します。複雑な検索条件は、この2つの関数を 複数回使用することにより構築できます。mrqand関数とmrqor関数は、mrgetbegin関数の引数として受け渡される、 検索条件識別子を返します。

mrqnot関数は、引数として検索条件識別子を指定し、検索条件の設定を逆転させます。mrqieq関数が使用される場合、 その関数が返す検索条件識別子をmrqnot関数に渡した場合、フィールド値がinteger値と等しくないものを検索条件と します。mrqnot関数から返される検索条件識別子は、mrqand関数、mrqor関数、mrqnot関数、mrgetbegin関数のうちの 1つに受け渡されます。 検索条件識別子を、mrqand関数、mrqor関数、mrqnot関数へ渡した場合、その検索条件識別子は破壊され、再使用 できなくなります。

これらの関数の一般的な形式を以下に示します。

       qual_desc = mrqand (qual_desc_1, qual_desc_2);

       qual_desc = mrqor (qual_desc_1, qual_desc_2);

       qual_desc = mrqnot (qual_desc);

最後に、mrqlst関数は、テーブル記述子と以前に取得したレコードポインタ（mrgetptr関数により）の配列をとり、その配列の レコードに検索条件を付加した、検索条件識別子を返します。

例

以下に示すプログラム、oldloans.cはmrqand関数とmrqor関数を使用して、特定の従業員に対して額が大きいか、未払いの ローンを検索します。

    #include    <mscc.h>

    #define     DATABASE   "repairs"

    #define     ARGNUMBER  4

    msmain   (int argc, char** argv)
    {
         addr     loans_tabdesc, loans_recdesc, name_attrdesc;
         addr     date_attrdesc, amount_attrdesc;
         char*    date_value;
         char*    amount_value;
         addr     qual, qual_2, retrieve_desc;
         char*    name;
         char*    date;
         char*    amount;

         if (argc != ARGNUMBER)
         {
              fprintf (stderr, "Usage: %s name date amount\n", argv[0]);
              msexit (1);
         }
         name = argv[1];
         date = argv[2];
         amount = argv[3];

         loans_tabdesc = mropen (DATABASE, "loans", 'r');

         loans_recdesc = mrmkrec (loans_tabdesc);

         name_attrdesc = mrngeta (loans_tabdesc, "name");
         date_attrdesc = mrngeta (loans_tabdesc, "date");
         amount_attrdesc = mrngeta (loans_tabdesc, "amount");

         date_value = mrspv (date_attrdesc);
         amount_value = mrspv (amount_attrdesc);

         qual = mrqcon ("=", name_attrdesc, mrcvt (name_attrdesc, name));
         qual = mrqand (qual, mrqcon (">", date_attrdesc,
                        mrcvt (date_attrdesc, date)));

         qual_2 = mrqcon (">", amount_attrdesc, mrcvt (amount_attrdesc, amount));
         qual_2 = mrqand (qual_2, mrqcon ("=", name_attrdesc,
                          mrcvt (name_attrdesc, name)));

         qual = mrqor (qual, qual_2);

         printf ("Loans to %s Greater Than %s or Older Than %s\n\n", name, amount, date);
         printf ("Date     Amount\n");
         retrieve_desc = mrgetbegin (qual, loans_recdesc, ADDRNIL);
         while (mrget (retrieve_desc))
         {
            mrcopyv (loans_recdesc, amount_attrdesc, amount_value);
            mrcopyv (loans_recdesc, date_attrdesc, date_value);

            printf ("%-22s%-12s\n", date_value, amount_value);
         }
         mrgetend (retrieve_desc);

         mrfree (date_value);
         mrfree (amount_value);
         mrfrrec (loans_recdesc);

         mrclose (loans_tabdesc);
    }

## 3.14 レコードの削除

mrdel関数とmrtdel関数は、引数にレコード記述子を指定し、レコードを削除します。mrdel関数は、削除に失敗した場合 呼出しプログラムを終了します。mrtdel関数は、削除に失敗した場合falseを、成功した場合にはtrueを返します。 通常、レコードは削除前に検索されます。対象レコードは、mrgetbegin関数と検索条件関数により初期化され、mrget関数で 特定されます。すべての削除が完了した場合には、Empressの内部バッファから、ファイルへ書き込むために、mrdelend関数 を呼び出す必要があります。

レコードを削除する、一般的な形式を以下に示します。

       mrdel (record_desc);

       flag = mrtdel (record_desc);

       mrdelend (record_desc);

例

以下に示すプログラム、delete.cは1992年5月より前に作成されたレコードを削除します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain ()
       {
            addr     loans_tabdesc, date_attrdesc,
                     loans_recdesc;
            addr     qual, retrieve_desc;
            char*    date_value;

            loans_tabdesc = mropen (DATABASE, "loans", 'u');

            loans_recdesc = mrmkrec (loans_tabdesc);
            date_attrdesc = mrngeta (loans_tabdesc, "date");
            qual = mrqcon ("<", date_attrdesc,
                           mrcvt (date_attrdesc, "1 May 1992"));

            retrieve_desc = mrgetbegin (qual, loans_recdesc, ADDRNIL);
            while (mrget (retrieve_desc))
                 mrdel (loans_recdesc);

            mrgetend (retrieve_desc);

            mrdelend (loans_recdesc);

            mrfrrec (loans_recdesc);

            mrclose (loans_tabdesc);
       }

## 3.15 レコードの更新

レコードを更新するには、2つのレコード記述子を取得する必要があります。最初のレコード記述子（更新前用）は、 mrmkrec関数によって取得します。次のレコード記述子（更新後用）は、mrcopyr関数によって、更新前用レコード記述子を コピーして作成されます。この関数は、引数に2つのレコード記述子を指定します。更新後用のレコード内のフィールド値は 、レコードの追加処理と同様に、mrputvs関数、mrputvi関数などを使用して変更します。レコードの更新は、mrput関数を 呼び出して行なわれます。この関数は、引数に更新前、更新後のレコード記述子を指定します。

更新が失敗する可能性がある場合（ユニークなフィールドに重複値を入力したり、ロックがかけられる場合）には、mrtput関数 を使用してください。この関数は、失敗した時にはfalseを、成功した時にはtrueを返します。

これらの関数の一般的な形式を以下に示します。

       mrcopyr (new_rec_desc, old_rec_desc);

       mrput (new_rec_desc, old_rec_desc);

       flag = mrtput (new_rec_desc, old_rec_desc);

例

以下に示すプログラム、interest.cはloansテーブル内のローンについて2%の利子を計算し、借りている額と利子を表示 します。また、現在額に利子を追加し、新しい合計を表示し、レコードを更新します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       extern      double     dollcvt ();

       msmain ()
       {
            addr      loans_tabdesc, pers_tabdesc,
                      loans_recdesc, pers_recdesc,
                      new_recdesc;
            addr      pname_attrdesc, lname_attrdesc,
                      date_attrdesc, amount_attrdesc;
            addr      qual, p_retrieve_desc,
                      l_retrieve_desc;
            double    amount, sum, newamount, interest;
            char*     name;
            char*     pname_value;
            char*     date_value;
            char*     amount_value;
            char      value[20];

            loans_tabdesc = mropen (DATABASE, "loans", 'u');
            pers_tabdesc = mropen (DATABASE, "personnel", 'r');

            loans_recdesc = mrmkrec (loans_tabdesc);
            new_recdesc = mrmkrec (loans_tabdesc);
            pers_recdesc = mrmkrec (pers_tabdesc);

            pname_attrdesc = mrngeta (pers_tabdesc, "name");
            lname_attrdesc = mrngeta (loans_tabdesc, "name");
            amount_attrdesc = mrngeta (loans_tabdesc, "amount");
            date_attrdesc = mrngeta (loans_tabdesc, "date");

            amount_value = mrspv (amount_attrdesc);
            pname_value = mrspv (pname_attrdesc);
            date_value = mrspv (date_attrdesc);

            p_retrieve_desc = mrgetbegin (ADDRNIL, pers_recdesc, ADDRNIL);
            while (mrget (p_retrieve_desc))
            {
                 mrcopyv (pers_recdesc, pname_attrdesc, pname_value);

                 printf ("\n\nMonthly Statement for %s:\n\n", pname_value);
                 printf ("Loan     Date Made     Interest Total\n");

                 sum = 0;
                 qual = mrqcon ("=", lname_attrdesc,
                                mrcvt (lname_attrdesc, pname_value));
                 l_retrieve_desc = mrgetbegin (qual, loans_recdesc, ADDRNIL);
                 while (mrget (l_retrieve_desc))
                 {
                      mrcopyv (loans_recdesc, amount_attrdesc, amount_value);

                      mrcopyv (loans_recdesc, date_attrdesc, date_value);
                      amount = dollcvt (amount_value);
                      interest = amount * 0.02;
                      newamount = amount + interest;
                      sum = sum + newamount;
                      printf ("$%-6.2f %s      $%-6.2f $%-6.2f\n",
                              amount, date_value, interest, newamount);

                      mrcopyr (new_recdesc, loans_recdesc);

                      sprintf (value, "$%-6.2f", newamount);
                      if (mrputvs (new_recdesc, amount_attrdesc, value))
                           mrput (new_recdesc, loans_recdesc);
                      else
                           fprintf (stderr, "Cannot convert\
                                newamount '%-6.2f'\n\
                                Update not done for %s %s\
                                %-6.2f\n", newamount,
                                pname_value, date_value, amount);
                 }
                 mrgetend (l_retrieve_desc);

                 if (sum > 0)
                      printf ("\nTotal now owing: $%-6.2f\n", sum);
                 else
                      printf ("\nNo loans outstanding.\n");
            }
            mrgetend (p_retrieve_desc);

            mrfree (pname_value);
            mrfree (date_value);
            mrfree (amount_value);
            mrfrrec (loans_recdesc);
            mrfrrec (new_recdesc);
            mrfrrec (pers_recdesc);

            mrclose (loans_tabdesc);
            mrclose (pers_tabdesc);
       }

       double dollcvt (char* string)     /* strip $ , * space from dollar amounts */
       {
            extern     double    atof ();
            char       c, buffer [20];
            char*      buf_ptr;

            for (buf_ptr = buffer ; (c = *string++) != '\0' ; )
               if (c != '$' && c != '*' && c != ' ' && c != ',')
                  *buf_ptr++ = c;
            *buf_ptr = '\0';
            return (atof (buffer));
       }

## 3.16 後で使用するレコードの保存

アプリケーションプログラムでは、異なる個所で同一のレコードに異なった操作をしなくてはならない場合があります。 一度レコードを検索したら、以下に示すことができます。

1. 直接操作できるように、そのレコードをメモリに保存する。

2. 以降の検索で、検索条件を構築しなくても良いように、そのレコードへのポインタを保存する。

レコード値が変更された場合には、変更前の検索条件ではレコードを再検索できないので、レコードまたはポインタを保存 することが、非常に役にたちます。

### 3.16.1 メモリへのレコード保存

レコードをメモリ内に保存するには、そのレコードのレコード記述子をコピーすることが伴います。この場合、スペースを使い 過ぎないように、数レコードに限って実行してください。レコードがロック状態で保たれている場合にも、メモリへのレコード 保存は便利です。

レコード記述子をコピーするには、mrmkrec関数を呼び出して、新しいレコード記述子を取得し、レコードの更新のように 、mrcopyr関数によって新しいレコード記述子にコピーします。これによってレコードがメモリ内にコピーされ、テーブルから 再検索することなく、いつでもアクセス可能となります。（通常は検索ループの中で、現在のレコードが後で必要になると 判断された場合に実行されます。）保存されたレコード記述子は、後のプログラム内でいつでも、mrcopyv関数などの 検索関数の1つに受け渡すことが可能です。レコードに対する処理が完了したら、mrfrrec関数を使用して、スペースを 解放してください。

### 3.16.2 レコードポインタの保存

レコードへのポインタを取得するには、引数にレコード記述子を指定する、mrgetptr関数を使用します(mrget関数で レコードを特定した後で）。単一のレコードを再検索するには、このポインタとレコード記述子をmrgetrec関数に渡します。 この関数は、レコードをテーブルから再度読込みます。複数のレコードを再度検索するには、レコードへのポインタ配列を 取得し、mrqlst関数に渡します。この関数は、その後mrgetbegin関数に渡す、検索条件記述子を返します。

mrgetrec関数は、実際はmrqlst関数を使用する特殊なケースです。この関数は、受け渡されるポインタをポインタ配列の 第一入力に割当て、この配列でmrqlst関数を呼び出し、mrqlst関数から返される検索条件記述子を、mrgetbegin関数 に渡します。この後、mrtget関数でレコードを検索し、最後にmrgetend関数でクリーンアップを行います。 関数が成功した場合は、レコード記述子は該当レコードを参照している、値検索関数へ受け渡すことが可能です。

mrgetrec関数は、1回の検索ループと考えられるものを設定しているので、複数の継続呼出しは、配列内にポインタを 記憶し、それ自身で検索ループを設定するよりも効率が悪くなります。

mrgetrec関数が使用された場合、可能であれば該当レコードはロックされます。（次の章を参照）この場合、mrgetrec関数 に指定したレコード記述子を使用して、mrulrec関数を呼び出して、明示的にロックを解除する必要があります。

## 3.17 レコードレベルロック

レコードレベルロックが、指定テーブルに対して設定されている場合、検索ループでレコードがアクセスされる度に、mrルーチン は自動的にそれぞれのレコードをロックします。この自動ロックは、次のレコードが検索された時点で解除されます。また、 mrgetend関数が実行された時点でも、解除されます。後の使用のためにレコードを保存し（前の章を参照）、他からの アクセスを防ぐために、ロックをしたままで保ちたい場合には、明示的にロックを行なう必要があります。

明示的にロックを行なうには、mrmkrec関数を使用して新しいレコード記述子を取得し、mrcopyr関数を使用して、正確に レコードを参照していることを確実にし、この新しいレコード記述子をmrlkrec関数に渡します。このレコードは、検索ループが 終了した後でも、ロックがかけられた状態になっています。後でこのレコードを参照するには、必ず新しいレコード記述子を 使用してください。

このレコードでの処理を終了したら、レコード記述子をmrulrc関数に渡して、ロックを解除し、レコード記述子が使用している スペースを、mrfrrec関数で解放してください。

テーブルロックまたはグループロックが設定されている場合でも、この関数は機能します。melkrec関数による検索ループ内の レコードロックは、既存のテーブルロックまたはグループロックに追加されます。

例

以下に示すプログラム、kilroy.cはレコードを保存し、ロックする一般的原則を解説します。employeesプログラムの 変更版であり、personnelテーブルからのkilroyのレコードを保存し、検索ループが終了した後で、名前と電話番号を 表示します。

       #include    <mscc.h>

       #define     DATABASE   "repairs"

       msmain()
       {
            addr     pers_tabdesc, name_attrdesc,
                     phone_attrdesc, pers_recdesc;
            addr     retrieve_desc, rec_desc_2;
            int      flag, employees;

            char*    name_value;
            char*    phone_value;

            pers_tabdesc = mropen (DATABASE, "personnel", 'r');

            pers_recdesc = mrmkrec (pers_tabdesc);
            name_attrdesc = mrngeta (pers_tabdesc, "name");
            phone_attrdesc = mrngeta (pers_tabdesc, "phone");

            name_value = mrspv (name_attrdesc);
            phone_value = mrspv (phone_attrdesc);

            printf ("Current Employees\n\n");
            printf ("Name           Phone\n\n");

            employees = flag = 0;
            retrieve_desc = mrgetbegin (ADDRNIL, pers_recdesc, ADDRNIL);
            while (mrget (retrieve_desc))
            {
                 mrcopyv (pers_recdesc, name_attrdesc, name_value);
                 mrcopyv (pers_recdesc, phone_attrdesc, phone_value);
                 printf ("%-12s%-12s\n", name_value, phone_value);
                 employees++;
                 if (strcmp (name_value, "Kilroy") == 0)
                 {
                      rec_desc_2 = mrmkrec (pers_tabdesc);
                      mrcopyr (rec_desc_2, pers_recdesc);
                      mrlkrec (rec_desc_2);
                      flag = 1;
                 }
            }
            mrgetend (retrieve_desc);
            printf ("\nTotal Number of Employees = %d\n\n",  employees);
            if (flag)
            {
                 mrcopyv (rec_desc_2, name_attrdesc, name_value);
                 mrcopyv (rec_desc_2, phone_attrdesc, phone_value);
                 printf ("%-12s%-12s\n", name_value, phone_value);

                 mrulrec (rec_desc_2);
                 mrfrrec (rec_desc_2);
            }
            mrfree (name_value);
            mrfree (phone_value);
            mrfrrec (pers_recdesc);

            mrclose (pers_tabdesc);
       }

## 3.18 トランザクション

トランザクションとは、データベース上にあり、明確にコミットされるまでデータベースに変更を与えない、一連の操作です。 トランザクションがキャンセルされた場合、データベースはトランザクションが開始される前の状態のままです。 トランザクションの解説については、Empress SQL: ユーザーズガイド を参照してください。

トランザクションは、mrtrstart関数によって開始されます。この関数は引数がなく、トランザクションの開始に成功した場合は trueを、失敗した場合はfalseを返します。失敗の場合は、通常トランザクションが既に実行中であることを意味します。

トランザクションは、mrtrcommit関数でコミットされ、mrtrcancel関数でキャンセルされます。これらの関数も引数がなく、 操作の成功を示すフラグを返します。

一般的な形式を以下に示します。

       flag = mrtrstart ();

       flag = mrtrcommit ();

       flag = mrtrcancel ();

セーブポイントをトランザクション内に設定できます。セーブポイントが設定された場合は、設定位置までロールバックする ことができます。関数の一般的な形式を以下に示します。

       flag = mrtrsave (savepoint);

       flag = mrtrrollback (savepoint);

エラーによって、トランザクション処理中に呼出しプログラムが終了された場合、トランザクションは、プログラム終了前に キャンセルされます。

## 3.19 表現式

mrルーチンによって、データベースのデータに対して直接、検索の定義と実行が可能となります。mrqcon関数（フィールド をファイルフォーマットの定数と比較）、mrqatr（2つのフィールドを比較）、mrqseq関数（フィールドを文字列定数と比較）など の関数は、検索条件の基礎を形成する単純なboolean表現式を定義するために使用されます。さらにmrqand関数、mrqor関数 、mrqnot関数などの関数は、単純な検索条件を結合して、複合検索条件を構築するために使用されます。これらの関数の 使用方法については、前の検索条件の章で解説しています。

表現式は、関数（演算子）と引数（オペランド）によって構築されます。ここで関数は、Empress演算子（+、-等）、Empress組込み関数、 ユーザ定義関数と演算子を含んでいます。またここでいう引数とは、演算子の関数とオペランドへの引数を含んでいます。 Empress演算子と組込み関数は、Empress SQLリファレンスのexpr以下で解説しています。ユーザ定義関数と演算子は、 Empressユーザ定義関数のマニュアルで解説しています。オペランドとは、定数、変数、フィールド値です。

mrルーチンを用いて構築する表現式は、データベースに照会することなく、直接評価されるか、テーブルからレコード を検索するための検索条件構築に使用されます。

この章では、複雑な表現式の、定義と実行に使用できる関数について解説します。

### 3.19.1 表現式の使用

すべての表現式構築関数は、mre...が接頭辞として使われています。これらについては、次のサブセクションで紹介します。

boolean表現式は、すべてmrqexpr関数を使用して、検索条件に直接変換されます。その一般的な形式を以下に示します。

       qual_desc = mrqexpr (expr_desc);

`expr_desc` expr_descは、表現式の記述子へのポインタです（mreend関数で取得）。qual_descは検索条件記述子へのポインタです。

表現式は、mrerun関数を使用して直接評価されます。その一般的な形式を以下に示します。

       result = mrerun (expr_desc);

resultは、表現式の結果へのポインタです。

### 3.19.2 表現式の構築

表現式は、スタックを使用して構築されます。表現式の構築は、mrebegin関数で開始され、表現式の記述子へのポインタを 返すmreend関数の呼出しで終了します。表現式が必要でなくなった場合は、その記述子はmrefree関数を使用して解放 しなくてはなりません。ただし、表現式をmrexpr関数で検索条件に変換した場合には、記述子は解放されます。表現式の 構築中にエラーが検出された場合は、構築処理を中止するためにmreabort関数を使用します。これらの関数の一般的な 形式を以下に示します。

       mrebegin ();

       expr_desc = mreend ();

       mrefree (expr_desc);

       mreabort ();

mrecons関数とmreivar関数は、引数として表現式に定数と変数を追加します。mrerecattr関数は、フィールド引数を表現式 に追加します。mrenull関数は、フィールド値がNULLであるかチェックするのに使用します。一般的な形式を以下に示します。

       flag = mrecons (string, type_desc);

       mreivar (ivar, type_desc);

       mrerecattr (record_desc, attr_desc);

       flag = mrenull (func);

type_descは、データタイプ記述子へのポインタです。stringは、外部フォーマットの定数へのポインタです。ivarは、変数への 間接ポインタです。funcは、関数へのポインタです。

関数は、mrefunc関数を使用して表現式に追加されます。関数が追加される場合には、必要な引数がスタック上になくては なりません。この関数の一般的な形式を以下に示します。

       flag = mrefunc (func, nargs);

funcは、関数名です。nargsは、関数のもつ引数の数です。

mrecvarg関数は、表現式のデータタイプを、指定のデータタイプに変換します。その一般的な形式を以下に示します。

       flag = mrecvarg (type_desc);

### 3.19.3 Empress値とC変数

データベースからの検索値または、Empressデータタイプへの変換値がC変数内に保存される場合は、変数が適切なタイプ である必要があります。EmpressとCデータタイプの対応表は、2章のEmpressとCデータタイプで提供します。これは、表現式 に関係するmreivar関数とmrerun関数に適用されます。

### 3.19.4 関数へのデータタイプの指定

引数として、データタイプを持つ関数（mrecons関数、mreivar関数、mrecvarg関数）は、Empressの汎用データタイプあるいは 、ユーザデータタイプを使用します。汎用データタイプについては、以下の値が、データタイプ引数として使用されます。

Table 3-2: 汎用データタイプの値

データタイプ | パラメータ
`GENERIC BINARY` | `msdtpar_gen_bin_p`
`GENERIC BOOLEAN` | `msdtpar_gen_bol_p`
`GENERIC INTEGER` | `msdtpar_gen_int_p`
`GENERIC FLOAT` | `msdtpar_gen_flt_p`
`GENERIC CHAR` | `msdtpar_gen_chr_p`
`GENERIC DATE` | `msdtpar_gen_dat_p`
`GENERIC DECIMAL` | `msdtpar_gen_dec_p`
`GENERIC EXTERNAL` | `msdtpar_gen_ext_p`
`GENERIC INTERVAL` | `msdtpar_gen_inv_p`

他のデータタイプについては、フィールドのデータタイプ記述子へのポインタを返す、mrgdtpar関数を呼び出すことで データタイプ記述子が取得できます。

### 3.19.5 サンプル

以下に、表現式の構築と評価を解説する8つの例を示します。

例 1

以下に示すプログラムは、単純な式（5-i)を評価します。この式ではiが1から5の範囲であり、その結果を表示します。

       /* Build and evaluate expression (5 -i), where i = 1 to 5 */

       #include     <mscc.h>

       msmain()
       {
            addr     result;
            addr     expr;
            long*    i;
            long     ival;

            i = &ival;

            mrebegin ();     /* begin building expression */

            mrecons ("5", dtpgint);
            mreivar ((addr)&i, dtpgint);
            mrefunc ("-", 2);

            expr = mreend();          /* expr is (5 - i) */

            for (*i = 1; *i <=5; ++(*i))
            {
                 result = mrerun (expr);     /* evaluate the expression */

                 printf ("%ld\n", *(long*) result);
            }
             mrefree (expr);          /* free the expression */
       }

例 2

以下に示すプログラムは、データベースrepairs中の、tabテーブル内のaフィールド（INTEGERタイプ)、 bフィールド（FLOATタイプ）の積を計算します。

       /* expression (attr1 * attr2) */

       #include     <mscc.h>

       char*        Database = "repairs";

       msmain()
       {
            addr     rec_desc;
            addr     mr_desc;
            addr     ret_desc;
            addr     a_des, b_des;
            addr     result;
            addr     expr;

            mr_desc = mropen (Database, "tab", 'r');

            a_des = mrngeta (mr_desc, "a"); /*attribute descriptors for */

            b_des = mrngeta (mr_desc, "b");   /* a and b. */

            rec_desc = mrmkrec (mr_desc);

            mrebegin();

            mrerecattr (rec_desc, a_des);     /* add attr a */
            mrerecattr (rec_desc, b_des);     /* add attr b */
            mrefunc ("*", 2);

            expr = mreend ();                 /* expr is (a*b)*/

            ret_desc = mrgetbegin (ADDRNIL, rec_desc, ADDRNIL);
            while (mrget (ret_desc))   /* for all the records in the table */
            {
                result = mrerun (expr);     /* evaluate expression */
                printf ("%f\n", *(double *) result );
            }
            mrgetend (ret_desc);
            mrefree (expr);
            mrfrrec (rec_desc);

            mrclose (mr_desc);
       }

例 3

以下に示すプログラムは、tabテーブル内のcフィールド（CHARタイプ）の値がNULLでない場合に、その値を表示します。

       /* print the values of records for which a field is not null */

       #include     <mscc.h>

       char*        Database = "repairs";

       msmain()
       {
            addr     rec_desc;
            addr     mr_desc;
            addr     ret_desc;
            addr     c_des;
            char*    result;
            addr     expr;
            addr     q;

            mr_desc = mropen (Database, "tab", 'r');

            c_des = mrngeta (mr_desc, "c");
            rec_desc = mrmkrec (mr_desc);

            mrebegin ();

            mrerecattr (rec_desc, c_des);     /* add attr c */

            if (!mrenull ("!=" ))     /* c != null */
            {
                 printf ("Can not put null checking operator in the expression\n");
                 mreabort ();          /* abort building expression */

                 msexit (1);
            }
            expr = mreend ();          /* expr is  c != null */

            q = mrqexpr (expr);        /* make qualification out of expr */

            ret_desc = mrgetbegin (q, rec_desc, ADDRNIL);

            while (mrget (ret_desc))
            {
                 printf ("%s\n", mrgetvs (rec_desc, c_des));
            }
            mrgetend (ret_desc);

            mrfrrec (rec_desc);

            mrclose (mr_desc);
       }

例 4

以下に示すプログラムは、repaursデータベースの、personnelテーブルの電話番号の最初の3桁を表示します。

       /* get the first 3 digits from the phone numbers for all employees */

       #include     <mscc.h>

       char*        Database = "repairs";

       msmain()
       {
            addr     rec_desc;
            addr     mr_desc;
            addr     ret_desc;
            addr     phone_des, name_des;
            char*    result;
            addr     expr;

            mr_desc = mropen (Database, "personnel", 'r');

            phone_des = mrngeta (mr_desc, "phone");
            name_des = mrngeta (mr_desc, "name");

            rec_desc = mrmkrec (mr_desc);

            mrebegin ();

            mrerecattr (rec_desc, phone_des);
                                /* add attr phone */
            mrecons ("1", dtpgint); /* start position for string extraction */
            mrecons ("3", dtpgint); /* number of chars to be get */

            mrefunc ("substr", 3);

            expr = mreend ();   /* expression is substr(phone,1,3) */
            ret_desc = mrgetbegin (ADDRNIL, rec_desc, ADDRNIL);
            while (mrget (ret_desc)) /* for all the records in the table */
            {
                 result = mrerun (expr);     /* evaluate expression */
                 printf ("%s %s\n", mrgetvs (rec_desc, name_des), result);
            }
            mrgetend (ret_desc);

            mrefree (expr);
            mrfrrec (rec_desc);

            mrclose (mr_desc);
       }

例 5

以下に示すプログラムは、クレジット限度を50%増加した額が、$1000を超える従業員名を表示します。

       /* find the names of the employees who will exceed a credit
          limit of $1000, if given 50% increase in credit. */

       #include     <mscc.h>

       char*        Database = "repairs";

       msmain()
       {
            addr     rec_desc;
            addr     mr_desc;
            addr     ret_desc;
            addr     credit_des, name_des;
            addr     q;
            addr     expr;

            mr_desc = mropen (Database, "personnel", 'r');

            credit_des = mrngeta (mr_desc, "credit_limit");
            name_des = mrngeta (mr_desc, "name");
            rec_desc = mrmkrec (mr_desc);

            mrebegin ();

            mrerecattr (rec_desc, credit_des);
            mrecons ("1.5", dtpgdec);
            mrefunc ("*", 2);     /* (1.5 * credit_limit) */
            mrecons ("1000", dtpgdec);
            mrefunc (">", 2);

            expr = mreend ();     /* expression built is
                                     (1.5 * credit_limit) > 1000 */

            q = mrqexpr (expr);   /* make qualification */

            ret_desc = mrgetbegin (q, rec_desc, ADDRNIL);

            while (mrget (ret_desc))     /* for all the records in the table */
                 printf ("%s\n", mrgetvs (rec_desc, name_des));

            mrgetend (ret_desc);

            mrfrrec (rec_desc);

            mrclose (mr_desc);
       }

例 6

以下に示すプログラムは、クレジット限度を$500増加した額が、$1000を超えた従業員名を表示します。

          /* find the names of all the employees whose credit limit
             will   become $1000 or more if given an increase of $500. */

          #include     <mscc.h>

          char*        Database = "repairs";

          msmain()
          {
               addr     rec_desc;
               addr     mr_desc;
               addr     ret_desc;
               addr     credit_des, name_des;
               addr     q;
               addr     expr;

               mr_desc = mropen (Database, "personnel", 'r');

               credit_des = mrngeta (mr_desc, "credit_limit");
               name_des = mrngeta (mr_desc, "name");
               rec_desc = mrmkrec (mr_desc);

               mrebegin ();

               mrecons ("$500", mrgdtpar (credit_des));
               mrerecattr (rec_desc, credit_des);
               mrefunc ("+", 2);      /* (credit_limit + $500) */
               mrecons ("1000", dtpgdec);
               mrefunc (">", 2);

               expr = mreend ();      /* (credit_limit + $500) = 1000 */

               q = mrqexpr (expr);

               ret_desc = mrgetbegin (q, rec_desc, ADDRNIL);

               while (mrget (ret_desc))
                    printf ("%s\n", mrgetvs( rec_desc, name_des));

               mrgetend (ret_desc);

               mrfrrec (rec_desc);

               mrclose (mr_desc);
          }

例 7

以下に示すプログラムは、クレジット限度を$500増加した額が、$1000を超えるMosca以外の従業員名を表示します。

       /* find the names of the employees who will exceed a credit limit of$1000,
          if given $500 increase in credit and the name is not Mosca. */

       #include     <mscc.h>

       char*        Database = "repairs";

       msmain()
       {
            addr     rec_desc;
            addr     mr_desc;
            addr     ret_desc;
            addr     credit_des, name_des;
            addr     q, qexpr, qname;
            addr     expr;

            mr_desc = mropen (Database, "personnel", 'r');

            credit_des = mrngeta (mr_desc, "credit_limit");
            name_des = mrngeta (mr_desc, "name");

            rec_desc = mrmkrec (mr_desc);
            mrebegin ();

            if (!mrecons ("$500", mrgdtpar (credit_des)))
            {
                 printf ("Can not put '500' on expression stack\n");
                 mreabort ();
                 msexit ();
            }
            mrerecattr (rec_desc, credit_des);

            if (! mrefunc ("+", 2))     /*(credit_limit + 500) */
            {
                 printf ("Can not put '+' operator on expression stack\n");
                 mreabort ();
                 msexit ();
            }
            if (! mrecons ("1000", dtpgdec))
            {
                 printf ("Can not put '1000' on expression stack\n");
                 mreabort ();
                 msexit ();
            }
            if (! mrefunc (">", 2))
            {
                 printf ("Can not put '>' on expression stack\n");
                 mreabort ();
                 msexit ();
            }
            expr = mreend ();     /* expression built is
                                     (1.5 * credit_limit) = 1000*/

            qexpr = mrqexpr (expr);     /* make qualification */

            qname = mrqcon ("!=", name_des, "Mosca");
            q = mrqand (qexpr, qname);

            ret_desc = mrgetbegin (q, rec_desc, ADDRNIL);

            while (mrget(ret_desc))    /* for all the records in the table */
                 printf ("%s\n", mrgetvs (rec_desc, name_des));

            mrgetend (ret_desc);

            mrfrrec (rec_desc);

            mrclose (mr_desc);
       }

例 8

以下に示すプログラム(swap.c)は、プログラムにコマンドライン引き数として与えられた、2つの文字列を交換するために PSMを使用する表現を評価します。プログラムは動的メモリ割付け関数を使用します。

|  |  | `mspsm_malloc` | PSM用にデータを格納するためにスペースを割付けます。
|  |  | `mrmalloc` | Empress RDBMSで使用するのに、データを格納するためにスペースを順に割付けます。
|  |  | `mrfree` | mrmallocで割付けたスペースを解放します。

mspsm_mallocでPSM内で割付けたスペースは、Empress RDBMSによって解放されます。

       /* PSM -  user defined function to exchange the content of two non-null strings */

       #include        <string.h>
       #include        <usrfns.h>

       void    swapptr (
                       char**         arg1p,
                       char**         arg2p)
       {
               char*   str1;
               char*   str2;

               str1 = (char*) mspsm_malloc (strlen (*arg2p) + 1);
               str2 = (char*) mspsm_malloc (strlen (*arg1p) + 1);
               strcpy (str1, *arg2p);
               strcpy (str2, *arg1p);
               *arg1p = str1;
               *arg2p = str2;
       }

emppsmccコマンドにより、ソースファイルをコンパイルし、共有オブジェクトを作成します。例えば、swap.cをコンパイルし、 swap.dllという共有オブジェクトを作成する場合。

       emppsmcc -o swap.dll swap.c

対話型SQLで、以下のSQLコマンドを実行します。

       CREATE MODULE module_swap
          PROCEDURE swap (INOUT GENERIC CHAR, INOUT GENERIC CHAR)

          EXTERNAL NAME swapptr;
       END MODULE;

       UPDATE MODULE module_swap
          FROM "/usr/joe/swap.dll"

そして、以下に示すプログラムで、上記で定義したPSMを呼び出します。

       /* Program that uses expression building mechanism to call
          "swap" -  user defined function to exchange the content
          of two non-null strings  */

       #include        <stdlib.h>
       #include        <string.h>
       #include        <usrfns.h>

       static  void    abort_expr (void);

       #define         DATABASE        "db"

       msmain (int     argc,
               char**  argv)
       {
               char*           arg1;
               char*           arg2;
               addr*           arg1p;
               addr*           arg2p;
               addr            expr;

               if (argc < 3)
               {
                       printf ("Usage: %s str1 str2\n", argv[0]);
                       exit (0);
               }

               mropdict (DATABASE, 'r');

               arg1 = mrmalloc (strlen (argv[1]) + 1);
               arg2 = mrmalloc (strlen (argv[2]) + 1);

               strcpy (arg1, argv[1]);
               strcpy (arg2, argv[2]);

               arg1p = (addr*)& arg1;
               arg2p = (addr*)& arg2;

               printf ("Before swap arg1 = %s; arg2 = %s\n",
                                       *arg1p, *arg2p);

               mrebegin ();
               mreivar (arg1p, (addr)dtpgchr);
               mreivar (arg2p, (addr)dtpgchr);

               if (!mrefunc ("swap", 2))
                       abort_expr ();

               expr = mreend ();

               mrerun (expr);

               printf ("After swap arg1 = %s; arg2 = %s\n",
                                       *arg1p, *arg2p);

               mrfree (arg1);
               mrfree (arg2);
               mrefree (expr);

               mrcldict ();
       }

       void    abort_expr (void)
       {
               mreabort ();
               mrcldict ();
               printf("Bad procedure name.\n");
       }

## 3.20 エラーメッセージ　- mrprterrとmrerrmsg

すべてのmrt...関数は、失敗時には変数mroperrにエラーコードがセットされます。この変数でエラー原因を調べることが できます。

mrprterr関数は、失敗時にエラーメッセージを表示します。この関数は標準エラー出力に対し、適切なメッセージを出力し 、呼出しプログラムを終了します。一般的な形式を以下に示します。

       mrprterr ();

mrerrmsg関数は、呼出しプログラムを終了せずに、適切なメッセージを返します。一般的な形式を以下に示します。

       string = mrerrmsg ();

## 3.21 BULKデータの操作

BULKデータの保存方法と操作方法を解説するために、デジタル化されたサンプルをBULKデータとして保存する データベースを使用して、入力音を保存サンプルと比較するプログラムを提供します。サンプル音は、6ビット音声コードと 2ビットのピッチコードを結合して、個々の音を表現する音声合成から取り出されたものと想定しています。個々の音は 1バイトの16進コードで表現されます。

サンプル音は、soundsという名前のデータベースに保存されています。テーブルは、以下のコマンドで作成します。

       CREATE voice_samples (id INTEGER, sample BULK (15,15,10,1));

BULKデータの、プライマリ記憶長は15バイト、オーバーフロー記憶長は10バイトです。

voice_samolesテーブルは、現在以下のサンプルを持っています。

       id    sample

       1     01 0c 25 2b 01 1f 03 03 27 12 23 0e 27 2a 10
       2     2b 24 1e 0d 3c 33 1f 03 03 27 14 19
       3     1b 02 18 26 2e 0d 1e 2d 12 18 0c 3b 2a

個々の音（1つの16進数で表現される）は、1バイトで保存されています。個々のサンプルのバイト長は、以下のように検索 されます。

       SELECT id, length (sample) FROM voice_samples;

       id    EXPRESSION_1

       1     15
       2     12
       3     13

BULK値は、外部フォーマット-16進数では上記のように表示されます。外部フォーマットでは、個々の16進数は、2バイトの 文字列として表現されます。EmpressではBULK値をバイナリで保存し、内部フォーマットで操作します。これはバイト数と 、それに続く実際のバイトで表現される多倍長整数です。値の最大サイズは、マシンのC long内に記憶される最大の整数 サイズです。内部フォーマットでBULKデータを操作すると、外部フォーマットで操作する場合の半分のメモリしか必要と しません。

例

以下に示すプログラム、voice.cは内部フォーマットのBULKデータへのアクセス方法を解説します。

       #include    <mscc.h>

       #define     database   "sounds"

       msmain()
       {
            int     sound;

            printf ("Enter sound to be searched, CTRL D to quit : ");
            while ((scanf ("%x", &sound) != EOF))
            {
                 printf ("Sample Id\n");
                 getids (sound);
                 printf ("\n\nEnter sound to be searched, CTRL D To quit : ");
            }
            printf ("\n");
       }

       getids (int sound)
       {
            addr              qual;
            addr              ret_desc;
            addr              rec_desc;
            addr              tbl_desc;
            addr              attrid;
            addr              attrsample;
            char              *table_name;
            long              sample_id;
            unsigned char     *sample;
            long              sample_size;
            int               i;

            /* open the table containing voice samples */

            if ((tbl_desc = mrtopen (database, "voice_samples", 'r')) == ADDRNIL)
            {
                 printf ("Can not open table 'voice_samples '\n");
                 return;
            }

            rec_desc = mrmkrec (tbl_desc);

            attrid = mrngeta (tbl_desc, "id");

            attrsample = mrngeta (tbl_desc, "sample");

            /* set to get all the tuples from the table */

            ret_desc = mrgetbegin (ADDRNIL, rec_desc, ADDRNIL);

            while (mrget (ret_desc))
            {
                 /* get sample value in internal representation */
                 sample = mrgeti (rec_desc, attrsample);

                 /* get the size of the sample, in number of bytes */
                 sample_size = *(long *)sample;

                 /* check if the given sound exists in the entire sample */
                 for (i = 0; i < sample_size; i++)
                      if (* (sample + sizeof (long) + i) == sound)
                      {
                           /* get sample id */
                           sample_id = mrgetvi (rec_desc, attrid);
                           printf ("%d\n", sample_id);
                           break;
                      }
            }

            mrgetend (ret_desc);

            mrfrrec (rec_desc);

            mrclose (tbl_desc);
       }

msdtfsta関数は、TEXTまたはBULKフィールドへの挿入や更新のパフォーマンスを向上させるために、使用することが できます。すべての可変長データタイプ(TEXT、BULK、NLSTEXT)については、mrputi関数を使用して、フィールドの 内部フォーマットのデータでEmpressの内部バッファに渡されます。.relファイル(TEXTおよびBULKデータタイプへの 第2パラメータに指定される)に入るデータの一部は、レコード識別子のバッファーの中で維持されます。 データがオーバーフローする場合、オーバーフローデータはディスク上のテンポラリファイルに書かれます。 mradd関数(レコードの追加)、mrput関数(レコードの更新)呼び出されると、実際のレコードはデータベースへ転送されます。 その場合、プライマリレコードおよびオーバーフローデータがディスクに書かれます。オーバーフローデータは テンポラリファイルから転送されます。プロセスが終了した場合、テンポラリファイルは削除されます。

オーバーフローデータを、格納するためにテンポラリファイルを使用することは、I/Oのオーバヘッドが多くなります。 この場合、データをより速く転送するためには、msdtfsta(true)関数を使用してください。もしセットされれば、 テンポラリファイルは、オーバーフローデータの格納のために使用されません。すなわち、挿入処理または更新処理は、 Empress内部バッファーからデータベースファイルへ直接転送されます。この処理は、msdtfsta(false)が呼び出されるまで、 継続します。

msdtfstaを使用している場合、mradd関数またはmrput関数が呼び出されるまで、BULKまたはTEXTデータが保存されている バッファが変更されないようにしてください。

それは、CREATE TABLE、ALTER TABLEなどのような任意のデータ定義言語(DDL)コマンドと共に使用されてはなりません 。よってDDLステートメントを実行するmscallルーチンを使用してはなりません。

## 3.22 データストリーミング

データストリーミング（TEXTまたはBULKデータ片の操作）は、大きなBULKデータ（BLOB）やTEXTデータに対して、 挿入、更新、検索を行なう場合には、とても有用な手段です。 データが巨大な場合、データストリーミング機能なしでは、データを保持するために巨大な量の、メモリスペースを割り 付けなければなりません。大きなメモリスペースの割付けが困難な場合、その解決はデータ片を操作することです。 この方法であれば、データ片に対してスペースを割付けるだけで済みます。

mrルーチンでは、6つの関数がデータストリーミング用に提供されています。

- mrbuktxtcrt
- mrsubbegin
- mrsubend
- mrsubputi
- mrsubgeti
- mrbuktxtlen

以降の例は、以下に示す基準で作成されました。

1. データストリーミングの有用性を示すために、データサイズを大きいものとします。

2. データはユーザによって、簡単に作成できるものとします。

ここでは、Empressリカバリーログを使用します。その目的は、簡単に大きなサイズのデータを作成できるからです。 ユーザはサンプルプログラムを実行する上で、ログを使用します。そして、自由に他のプログラムに変更できるように作成 されています。

以下に示すように、データベースをセットアップします。

1. - tabzeroファイル中の、変数MSDBRECOVERYLOG1にログファイル名をセットします。
   - emprliniコマンドを実行し、ログファイルを作成します。

   以下のように、変数MSDBRECOVERYLOG1を設定したとします。

          /usr/sounds/log1

   emrliniコマンドを実行します。,

          emprlini sounds

   以下に示す、物理ファイルが作成されたことを確認できます。

          /usr/sounds/log1.000

2. プライマリ記憶長0の、フィールドを持つlogsテーブルを作成します。

          CREATE logs (log_key INTEGER, log_data BULK (20,0,1024,1));

3. データストリーミングは、整合性チェックを0に設定してください。（変数MSVALIDSIZEを0にします。）もしも 整合性チェックを0にせずにテーブルを作成した場合、以下のコマンドを実行することで、整合性を変更する ことができます。

          empadm sounds vsize logs -000

### 3.22.1 データの挿入例

プログラムinsert_in _chunk.cは、データ片にデータを挿入する方法を解説します。Empressリカバリーログのデータは、logs テーブル内の、BULKデータタイプを持ったlog_dataフィールドに保存されています。BULKデータはリカバリーログにより 保存されていきます。またこのデータはマシンのメモリより大きくなる場合があります。

以下に、データ片に挿入するプログラムの概要を示します。

       テーブルをオープンし、スペースを割当てます。
               table_desc = mrtopen (database_name, table_name, mode);
               record_desc = mrmkrec (table_desc);
               attr_desc = mrngeta (table_desc, attr_name);
               ...
               bulk_attr_desc = mrngeta (table_desc, attr_name);

       更新処理の初期化を行います。
               mrsubbegin (record_desc);

       レコード中のフィールドに、内部フォーマットのデータを割当てます。
               mrputi (record_desc, attr_desc, var_ptr);
               ...

       オーバフローファイルに挿入する、スペースサイズを定義します。
               mrbuktxtcrt (record_desc, bulk_attr_desc, size);

       テーブルにレコードを挿入します。
               mrtadd (record_desc);

               LOOP
                       BULKフィールドのデータを更新します。
                       mrsubputi (record_desc, bulk_attr_desc, bulk_attr_structure, offset);
               END LOOP

       更新処理の終了を宣言します。
               mrsubend (record_desc);

       リソースをクリーンアップします。
               mrfrrec (record_desc);
               mrclose(table_desc);

以下に示すプログラムは、insert_in_chenks.cです。

       /*
          This program will read from a stream and insert into the database
       */

       #include <mscc.h>
       #include <string.h>
       #include <stdlib.h>
       #include <sys/errno.h>

       #define USAGE   "USAGE:  %s  size_of_file_to_be_inserted  log_key_value\n"

       static  int     read_and_insert_data (
                                       long            size_of_file,
                                       long            key_value);

       /* program will write in 1K chunks */
       #define SIZE_OF_DATA_CHUNK      1024

       /* attribute names */
       static  char*   attr_name[]= {"log_key","log_data"};

       /* Definition of Empress' bulk attribute */
       struct  bulk    {
                       long            size_of_bulk;
                       unsigned char   data[1];
       };

       int     msmain(
                       int     argc,
                       char*   argv[])
       {
               long    key_value = 0L;
               long    size_value = 0;
               int     res;

               if (argc == 3 )
               {
                       size_value = (long) atoi (argv[1]);
                       key_value = (long) atoi (argv[2]);
                       res = read_and_insert_data (size_value, key_value);
               }
               else
               {
                       fprintf (stderr, USAGE, argv[0], argv[0]);
                       res = 1;
               }
               return res;
       }

       /*
        This routine will read data from an input stream and store it in the
        database
       */
       static  int     read_and_insert_data (
                                       long    size_of_file,
                                       long    key_value)
       {
               addr    tab_des;
               addr    record_des;
               addr    attr_des[2];
               long    flag;
               unsigned char*  input_data;
               struct  bulk*   bulk_attr_structure;
               long    offset;
               long    total_bytes;
               int     bytes_read;

               /* open database table */
               if ((tab_des = mrtopen ("sounds", "logs", 'u')) == ADDRNIL)
               {
                       fprintf(stderr,"Can't open table logs\n");
                       return 1;
               }

               /* Allocating only enough space for 1 bulk chunk */
               if ((input_data = (unsigned char *) malloc (
                               sizeof (long) + SIZE_OF_DATA_CHUNK)) == 0)
               {

                       fprintf(stderr,"Unable to allocate data space\n");
                       mrclose(tab_des);
                       return 1;
               }
               bulk_attr_structure = (struct bulk *) input_data;

               /* Allocate a record descriptor */
               if ((record_des = mrmkrec (tab_des)) == ADDRNIL)
               {
                       fprintf(stderr,"Unknown error, check mr error\n");
                       mrclose(tab_des);
                       free (input_data);
                       return 1;
               }

               /* Get the attribute descriptors */
               if ((attr_des[0] = mrngeta (tab_des, attr_name[0])) == ADDRNIL)
               {
                       fprintf (stderr,
                               "Problem getting the attribute descriptor for %s\n",
                               attr_name[0]);
                       mrfrrec (record_des);
                       mrclose(tab_des);
                       free (input_data);
                                                   return 1;
               }
               if ((attr_des[1] = mrngeta (tab_des, attr_name[1])) == ADDRNIL)
               {
                       fprintf (stderr,
                               "Problem getting the attribute descriptor for %s\n",
                               attr_name[1]);
                       mrfrrec (record_des);
                       mrclose(tab_des);
                       free (input_data);
                       return 1;
               }
               /* Initiate a start of an update operation */
               mrsubbegin (record_des);

               /* Insert the key into the database */
               if (!mrputi (record_des, attr_des[0], (addr) &key_value))
               {
                       fprintf (stderr,
                               "Problem: Can't insert key %d\n", key_value);
                       mrfrrec (record_des);
                       mrclose(tab_des);
                       free (input_data);
                       return 1;
               }

               /* Define a size of a space for insert in an overflow (.dtf) file.*/
               flag = mrbuktxtcrt (record_des, attr_des[1], size_of_file);
               if (flag != 0)
               {
                       fprintf (stderr,
                               "ERROR could not get %d bytes for buffer flag='%ld'\n",
                               size_of_file, flag);
                       mrfrrec (record_des);
                       mrclose(tab_des);
                       free (input_data);
                       return 1;
               }
               else
                       fprintf (stderr,
                                       "Creates %d bytes in the overflow file\n",
                                       size_of_file);

               /* Insert a record into a given table */
               if (!mrtadd (record_des))
               {
                       fprintf (stderr, "Insert a record failed\n");
                       mrfrrec (record_des);
                       mrclose(tab_des);
                       free (input_data);
                       return 1;
               }

               /* Update a given record with a partial bulk chunk */
               bytes_read = read (fileno(stdin), bulk_attr_structure->data,
                               SIZE_OF_DATA_CHUNK);
               bulk_attr_structure->size_of_bulk = (long) bytes_read;
               total_bytes = offset = 0L;
               while (bytes_read != 0)
               {
                       total_bytes = total_bytes + (long) bytes_read;
                       if ((flag = mrsubputi (record_des, attr_des[1],
                                               bulk_attr_structure,
                                               offset)) < 0L)
                       {
                               fprintf (stderr, "Update for bulk attribute at");
                               fprintf (stderr," offset %ld was not successful",
                                               offset);
                               fprintf (stderr," flag='%ld'\n",
                                               flag);
                               break;
                       }
                       else
                               fprintf (stderr,"Total bytes read in %d\n",
                                               total_bytes);
                       /* Set an offset value to insert the next chunk */
                       offset = offset + (long) bytes_read;

                       /* Read the next chunk for stdin */
                       bytes_read = read (fileno(stdin),
                               bulk_attr_structure->data,
                               SIZE_OF_DATA_CHUNK);

                       bulk_attr_structure->size_of_bulk = (long) bytes_read;
               }
               mrsubend (record_des);

               /* cleanup */
               mrfrrec (record_des);
               mrclose(tab_des);
               free (input_data);
               return 0;
       }

insert_in_chunkを使用して、リカバリーログファイルlog1.000から、logsテーブルにデータを挿入します。

       cp  /usr/sounds/log1.000  log

       insert_in_chunks 23454 1 < log

23454は、ログファイルのサイズで、1は、logsテーブルのlog_keyフィールドに挿入するデータ値です。

### 3.22.2 データの検索例

プログラム、retrive_in_chunk.cは、データ片の検索について解説します。

以下に、データ片の検索に関する、一般的な概要を示します。

       テーブルをオープンし、リソース用のスペースを確保します。
               table_desc = mrtopen (database_name, table_name, mode);
               record_desc = mrmkrec (table_desc);
               attr_desc = mrngeta (table_desc, attr_name);
               ...
               bulk_attr_desc = mrngeta (table_desc, attr_name);

       フィールド値と、Cのinteger値を比較します。
               qual = mrqieq (attr_desc, (int) key_value);

       レコードの検索条件を定義します。
               ret_desc = mrgetbegin (qual, record_desc, ADDRNIL)

       レコードを検索します。
               mrtget (ret_desc)

       BULKデータのを取得サイズを定義します。
               mrbuktxtlen (record_des, attr_desc);

               LOOP
                       BULKフィールドのデータ片を取得します。
                       mrsubgeti (     record_desc,
                                       bulk_attr_desc,
                                       bulk_attr_structure,
                                       offset,
                                       bytes_to_read_after_offset);
               END LOOP

       リソースをクリーンアップします。
               mrgetend (ret_desc);
               mrfrrec (record_desc);
               mrclose(tab_des);

以下に示すプログラムは、retrieve_in_chunk.c:

       /*
          This program will retrieve data from the database
          and write the results to an output stream.
       */

       #include <mscc.h>
       #include <string.h>
       #include <stdlib.h>
       #include <sys/errno.h>

       #define USAGE   "USAGE:  %s  log_key_value\n"

       static  int     retrieve_and_write (long        key_value);

       /* Program will read in 1K chunks */
       #define SIZE_OF_DATA_CHUNK      1024

       /* Attribute names */
       static  char*   attr_name[]= {"log_key","log_data"};

       /* Definition of Empress' bulk attribute */
       struct  bulk    {
                       long            size_of_bulk;
                       unsigned char   data[1];
       };

       int     msmain(
                       int     argc,
                       char*   argv[])
       {
               long    key_value = 0L;
               long    size_value = 0;
               int     res;

               if (argc == 2)
               {
                       key_value = (long) atoi (argv[1]);
                       res = retrieve_and_write (key_value);
               }
               else
               {
                       fprintf (stderr, USAGE, argv[0], argv[0]);
                       res = 1;
               }
               return res;
       }

       /* This routine will retrieve a bulk record based upon the key passed to it
          and write the bulk record to stdout */

       static  int     retrieve_and_write (long        key_value)
       {
               addr    tab_des;
               addr    record_des;
               addr    attr_des[2];
               unsigned char*  output_data;
               struct  bulk*   bulk_attr_structure;
               addr    ret_des;
               addr    qual;
               long    db_key;
               long    offset;
               long    flag;
               long    whats_left;
               long    total_bytes;

               /* Open database table */
               if ((tab_des = mrtopen ("sounds", "logs", 'r')) == ADDRNIL)
               {
                       fprintf(stderr,"Can't open table logs\n");
                       return 1;
               }

               if ((output_data = (unsigned char *) malloc (
                               sizeof (long) + SIZE_OF_DATA_CHUNK)) == 0)
               {

                       fprintf(stderr,"Unable to allocate data space\n");
                       mrclose(tab_des);
                       return 1;
               }
               bulk_attr_structure = (struct bulk *) output_data;

               /* Allocate a record descriptor */
               if ((record_des = mrmkrec (tab_des)) == ADDRNIL)
               {
                       fprintf(stderr,"Unknown error, check mr error\n");
                       mrclose(tab_des);
                       free (output_data);
                       return 1;
               }

               /* Get the attribute descriptors */
               if ((attr_des[0] = mrngeta (tab_des, attr_name[0])) == ADDRNIL)
               {
                       fprintf (stderr,
                               "Problem getting the attribute descriptor for %s\n",
                               attr_name[0]);
                       mrfrrec (record_des);
                       mrclose(tab_des);
                       free (output_data);
                       return 1;
               }
               if ((attr_des[1] = mrngeta (tab_des, attr_name[1])) == ADDRNIL)
               {
                       fprintf (stderr,
                               "Problem getting the attribute descriptor for %s\n",
                               attr_name[1]);
                       mrfrrec (record_des);
                       mrclose(tab_des);
                       free (output_data);
                       return 1;
               }

               qual = mrqieq (attr_des[0], (int) key_value);
               if ((ret_des = mrgetbegin (qual, record_des, ADDRNIL)) == ADDRNIL)
               {
                       fprintf (stderr, "Problem: Unable to perform 'mrgetbegin'\n");
                       mrfrrec (record_des);
                       mrclose(tab_des);
                       free (output_data);
                       return 1;
               }

               /* Get record qualified with the desired key. */
               offset = 0L;
               if (mrtget (ret_des) == 1)
               {
                       whats_left =
                               mrbuktxtlen (record_des, attr_des[1]);

                       /* If the size of the bulk data is smaller than
                          the data chunk, just write it out */
                       if (whats_left > 0  && whats_left < SIZE_OF_DATA_CHUNK)
                       {
                               flag = mrsubgeti (record_des,
                                       attr_des[1],
                                       & bulk_attr_structure,
                                       offset, whats_left);
                               if (flag > 0)
                               {
                                       write (fileno (stdout),
                                               bulk_attr_structure->data,
                                               whats_left);
                                       fprintf (stderr, "Wrote %d bytes to stdout\n",
                                                       whats_left);
                               }
                               else
                                       fprintf (stderr,
                                               "Problem with 'mrsubgeti' at line %d\n",
                                                __LINE__);
                       }
                       else if (whats_left > 0) /* Read/write out the blocks of data */
                       {
                               while (whats_left > SIZE_OF_DATA_CHUNK)
                               {
                                       flag = mrsubgeti (record_des,
                                               attr_des[1],
                                               & bulk_attr_structure,
                                               offset, SIZE_OF_DATA_CHUNK);
                                       whats_left -= SIZE_OF_DATA_CHUNK;
                                       offset += SIZE_OF_DATA_CHUNK;
                                       if (flag > 0)
                                       {
                                               write (fileno (stdout),
                                                       bulk_attr_structure->data,
                                                       SIZE_OF_DATA_CHUNK);
                                               fprintf (stderr,
                                                       "Wrote %d bytes to stdout\n",
                                                       SIZE_OF_DATA_CHUNK);
                                       }
                                       else
                                               fprintf (stderr,
                                               "Problem with 'mrsubgeti' at line %d\n",
                                                __LINE__);
                               }
                               flag = mrsubgeti (record_des,
                                       attr_des[1],
                                       & bulk_attr_structure,
                                       offset, whats_left);
                               if (flag > 0)
                               {
                                       write (fileno (stdout),
                                               bulk_attr_structure->data,
                                               whats_left);
                                       fprintf (stderr, "Wrote %d bytes to stdout\n",
                                               whats_left);
                               }
                               else
                                       fprintf (stderr,
                                       "Problem with 'mrsubgeti' at line %d\n",
                                       __LINE__);
                       }
                       else
                               fprintf (stderr, "Problem with 'mrbuktxtlen'\n");
               }
               else
                       fprintf (stderr, "Record for %d key was not found\n",
                               key_value);
               mrgetend (ret_des);
               mrfrrec (record_des);
               mrclose(tab_des);
               free (output_data);
               fflush (stdout);
               return ((flag < 0) ? 1 : 0);
       }

retrieve_in_chunksを実行して、logsテーブルからリカバリーログを検索します。

       retrieve_in_chunks 1 > bulkfile1

1は、logsテーブル中のlog_keyフィールド値で、bulkfile1は、検索結果を保存するファイル名です。

### 3.22.3 データの更新例

プログラムupdate_chunk.cは、BULKデータを更新する方法を解説します。

以下に、BULKデータの更新に関する、一般的な概要を示します。

       テーブルをオープンし、リソース用のスペースを確保します。
               table_desc = mrtopen (database_name, table_name, mode);
               record_desc = mrmkrec (table_desc);
               new_record_desc = mrmkrec (table_desc);
               attr_desc = mrngeta (table_desc, attr_name);
               ...
               bulk_attr_desc = mrngeta (table_desc, attr_name);

       フィールド値とCのintegetr値を比較します。
               qual = mrqieq (attr_desc, (int) key_value);

       レコードの検索条件を定義します。
               ret_desc = mrgetbegin (qual, record_desc, ADDRNIL)

       レコードを検索します。
               mrtget (ret_desc)

       レコードを、更新用にコピーします。
               mrcopyr (new_record_desc, record_desc);

       更新するデータのデータサイズを定義します。
               mrbuktxtcrt (new_record_desc, bulk_attr_desc, size);

       レコードを更新します。
       	mrput (new_record_desc, record_desc);

       更新処理の初期化を行います。
               mrsubbegin (new_record_desc);

               LOOP
                       BULKフィールド値の更新を行います。
                       mrsubputi (new_record_desc, bulk_attr_desc, bulk_attr_structure, offset);
               END LOOP

       更新処理の終了を宣言します。
               mrsubend (new_record_desc);

       リソースをクリーンアップします。
               mrgetend (ret_des);
               mrfrrec (record_des);
               mrfrrec (new_record_des);
               mrclose(tab_des);

以下に示すプログラムは、update_chunk.cです。

       /*
       This program will read data from a stream and
       update the bulk attribute starting from the defined offset
       */

       #include <mscc.h>
       #include <string.h>
       #include <stdlib.h>
       #include <sys/errno.h>

       #define USAGE   "USAGE:  %s  size_of_file  log_key_value offset\n"

       static  int     read_and_update_data (
                                  long            size_of_file,
                                  long            key_value,
                                  long            offset);

       /* program will write in 1K chunks */
       #define SIZE_OF_DATA_CHUNK      1024

       /* attribute names */
       static  char*   attr_name[]= {"log_key","log_file"};

       /* Definition of Empress' bulk attribute */
       struct  bulk    {
                  long            size_of_bulk;
                  unsigned char   data[1];
       };

       int     msmain(
                  int     argc,
                  char*   argv[])
       {
          long    key_value = 0L;
          long    offset = 0L;
          long    size_value = 0;
          int     res;

          if (argc == 4 )
          {
                  size_value = (long) atoi (argv[1]);
                  key_value = (long) atoi (argv[2]);
                  offset = (long) atoi (argv[3]);
                  res = read_and_update_data (size_value, key_value, offset);
          }
          else
          {
                  fprintf (stderr, USAGE, argv[0], argv[0]);
                  res = 1;
          }
          return res;
       }

       /*
       This routine will read data from an input stream and store it in the
       database
       */
       static  int     read_and_update_data (
                                  long    size_of_file,
                                  long    key_value,
                                  long    offset)
       {
          addr    tab_des;
          addr    record_des;
          addr    record_des2;
          addr    attr_des[2];
          long    flag;
          unsigned char*  input_data;
          struct  bulk*   bulk_attr_structure;
          addr    ret_des;
          addr    qual;
          long    total_bytes;
          int     bytes_read;

          /* open database table */
          if ((tab_des = mrtopen ("sounds", "logs", 'u')) == ADDRNIL)
          {
                  fprintf(stderr,"Can't open table logs\n");
                  return 1;
          }

          /* Allocating only enough space for 1 bulk chunk */
          if ((input_data = (unsigned char *) malloc (
                          sizeof (long) + SIZE_OF_DATA_CHUNK)) == 0)
          {

                  fprintf(stderr,"Unable to allocate data space\n");
                  mrclose(tab_des);
                  return 1;
          }
          bulk_attr_structure = (struct bulk *) input_data;

          /* Allocate a record descriptor */
          if ((record_des = mrmkrec (tab_des)) == ADDRNIL)
          {
                  fprintf(stderr,"Unknown error, check mr error\n");
                  mrclose(tab_des);
                  free (input_data);
                  return 1;
          }

          if ((record_des2 = mrmkrec (tab_des)) == ADDRNIL)
          {
                  fprintf(stderr,"Unknown error, check mr error\n");
                  mrclose(tab_des);
                  free (input_data);
                  return 1;
          }

          /* Get the attribute descriptors */
          if ((attr_des[0] = mrngeta (tab_des, attr_name[0])) == ADDRNIL)
          {
                  fprintf (stderr,
                          "Problem getting the attribute descriptor for %s\n",
                          attr_name[0]);
                  mrfrrec (record_des);
                  mrclose(tab_des);
                  free (input_data);
                  return 1;
          }
          if ((attr_des[1] = mrngeta (tab_des, attr_name[1])) == ADDRNIL)
          {
                  fprintf (stderr,
                          "Problem getting the attribute descriptor for %s\n",
                          attr_name[1]);
                  mrfrrec (record_des);
                  mrclose(tab_des);
                  free (input_data);
                  return 1;
          }

          qual = mrqieq (attr_des[0], (int) key_value);
          if ((ret_des = mrgetbegin (qual, record_des, ADDRNIL)) == ADDRNIL)
          {
                  fprintf (stderr, "Problem: Unable to perform 'mrgetbegin'\n");
                  mrfrrec (record_des);
                  mrclose(tab_des);
                  free (input_data);
                  return 1;
          }

          /* get record qualified with the desired key. */
          if (mrtget (ret_des) == 1)
          {
               mrcopyr (record_des2, record_des);

               flag = mrbuktxtcrt (record_des2, attr_des[1], size_of_file+offset);
               if (flag != 0)
               {
                  fprintf (stderr,
                          "ERROR could not get %d bytes for buffer flag='%ld'\n",
                          size_of_file, flag);
                  mrfrrec (record_des);
                  mrclose(tab_des);
                  free (input_data);
                  return 1;
               }
               else
                  fprintf (stderr,
                                  "Creates %d bytes in the overflow file\n",
                                  size_of_file);

               mrput (record_des2, record_des);

               /* Initiate a start of an update operation */
               mrsubbegin (record_des2);

               /* Update a given record with a partial bulk chunk */
               bytes_read = read (fileno(stdin), bulk_attr_structure->data,
                          SIZE_OF_DATA_CHUNK);
               bulk_attr_structure->size_of_bulk = (long)bytes_read;
               total_bytes = 0L;
               while (bytes_read != 0)
               {
                       total_bytes = total_bytes + (long) bytes_read;
                       if ((flag = mrsubputi (record_des2, attr_des[1],
                                               bulk_attr_structure,
                                               offset)) < 0L)
                       {
                               fprintf (stderr, "Update for bulk attribute at");
                               fprintf (stderr," offset %ld was not successful",
                                               offset);
                               fprintf (stderr," flag='%ld'\n",
                                               flag);
                               break;
                       }
                       else
                               fprintf (stderr,"Total bytes read in %d\n",
                                               total_bytes);
                       /* Set an offset value to insert the next chunk */
                       offset = offset + (long) bytes_read;

                       /* Read the next chunk from stdin */
                       bytes_read = read (fileno(stdin),
                               bulk_attr_structure->data,
                               SIZE_OF_DATA_CHUNK);
                       bulk_attr_structure->size_of_bulk = (long) bytes_read;
               }
               mrsubend (record_des2);
       }
          else
                  fprintf (stderr, "Record for %d key was not found\n",
                          key_value);

          mrgetend (ret_des);

          /* cleanup */
          mrfrrec (record_des);
          mrfrrec (record_des2);
          mrclose(tab_des);
          free (input_data);
          return 0;
       }

update_chunkを実行して、logsテーブル中のBULKデータを更新します。

       update_chunk 23454 2 100  < bulkfile1

23454は、bulkfile1ファイルのサイズです。2はlogsテーブル中の、更新するデータが持つlog_keyフィールド値です。先頭から 100バイト以降のデータが更新されます。

BULKデータがいくつかの固定サイズを持っている場合(例えば固定サイズのイメージファイル)、より単純でより効率的な方法 でデータ片の中のBULKデータを更新することができます。例えば、データベース中に画像ファイルを保存している場合には、 さまざまなサイズの画像ファイルで、更新する必要があります。

以下に、さまざまな固定サイズを持つ、BULKデータの一般的な更新処理の概要を示します。

    テーブルをオープンし、リソース用のスペールを確保します。
            table_desc = mrtopen (database_name, table_name, mode);
            record_desc = mrmkrec (table_desc);
            attr_desc = mrngeta (table_desc, attr_name);
            ...
            bulk_attr_desc = mrngeta (table_desc, attr_name);

    フィールド値と、Cのinteger値を比較します。
            qual = mrqieq (attr_desc, (int) key_value);

    レコードの検索条件を定義します。
            ret_desc = mrgetbegin (qual, record_desc, ADDRNIL)

    レコードを取得します。
            mrtget (ret_desc)

    更新処理の開始を宣言します。
            mrsubbegin (record_desc);

            LOOP
                    BULKフィールドのデータを更新します。
                    mrsubputi (record_desc, bulk_attr_desc, bulk_attr_structure, offset);
            END LOOP

    更新処理の終了を宣言します。
            mrsubend (record_desc);

    リソースをクリーンアップします。
            mrgetend (ret_des);
            mrfrrec (record_des);
            mrclose(tab_des);