# CHAPTER 2: プリコンパイラ全般のトピックス

## 2.1 はじめに

この章ではSQL プリコンパイラの使用に関連したトピックスと処理方法について 説明します。 以下のような項目が含まれます。

- データベースとテーブルへのアクセス
- 複数レコードの操作
- トランザクション
- プログラム変数の使用
- SQL コミュニケーションエリア(SQLCA)
- エラー処理
- 制約
- プリコンパイラの呼び出し

これらの説明は、 SQL プリコンパイラとC プログラムに埋め込んだ Empress SQLコマンド だけに適用されます。

## 2.2 データベースとテーブルへのアクセス

SQL プリコンパイラは、 C プログラム中からEmpressデータベースのテーブルに対しての 操作を実行することが可能です。 アクセスされるテーブルは、デフォルトのデータベースとして解釈され、 特別に指定しない限り、操作はデフォルトデータベースの上で行われます。

デフォルトデータベース以外のデータベーステーブルは、SQLプリコンパイラ コマンドを使用してテーブル名の前にデータベース名を指定してアクセスすることが できます。 データベース名の指定は、データベース名の後にコロン"."を入れます。

データベースをデフォルトとして、指定するには`DATABASE IS`コマンドを 発行します。 このコマンドを発行しない場合、デフォルトデータベースとして認識されず、 すべての各テーブル名の前にデータベース名を指定しなければなりません。 どのデータベースにおいても`DATABASE IS`コマンドを使用することにより いつでもデフォルトに設定できます。

コマンドを実行するごとに 必要なテーブルはすべてオープンされているか、 また適切なモード(read および update)で オープンされたかどうかをプリコンパイラは確認します。 必要なテーブルがオープンされていない場合、 指定したSQLコマンドに対応するモードでテーブルをオープンします。

テーブルは、`OPEN_TABLE`コマンドによってオープンすることができます。 オープンしたテーブルは、明示的にクローズするか、 あるいは`EXIT`コマンドを実行するまで オープンされた状態が維持されます。

テーブルは、`CLOSE_TABLE`コマンドによってクローズすることができます。 `CLOSE_TABLE`コマンドにテーブル名を指定しない場合、 オープンされているすべてのテーブルはクローズされます。

## 2.3 複数レコードの操作

SQLプリコンパイラは、C 言語プログラムから Empressデータベース上のテーブルに対し、 データの格納、検索、削除が可能です。 サポートされているコマンドは `UPDATE`、`DELETE` 、`SELECT INTO` です。 `UPDATE` と `DELETE` コマンドは、 `WHERE`句に指定した条件を満たす、 すべてのレコードに対して通常の処理を実行します。 `SELECT INTO`コマンドは、 `WHERE`句の条件に一致する最初のレコードだけに処理が実行されます。

### 2.3.1 カーソル

SQLプリコンパイラは、指定されたコンテキスト内の複数レコードを 選択的に操作ができます。 またレコードは、選択的に更新または削除が可能で、 複数レコードに含まれるデータは、 プログラムで処理するために取得できます。

このような操作は、カーソルを使用することで実現します。 カーソルを使用することで`SELECT`ステートメントに対応した 複数の行のコンテキストを定義し、これをカーソルと関連付けることで 処理する行を制御することができます。 カーソルはコンテキスト中の1レコードを差し、カーソルを"移動"することで コンテキスト中の行をカレントとして処理することができます。

### 2.3.2 カーソルの宣言

カーソルを使用する場合、それを使用するための前処理が必要になります。 これらの前処理は、カーソルの宣言とカーソルのオープンです。

カーソルは、`DECLARE CURSOR`コマンドで宣言し、 カーソルの宣言はカーソル名の割り当てと`SELECT` ステートメント に対応したカーソルと関連付けるコンテキストを定義します。 カーソルを扱うコマンドの前にカーソルの宣言をする必要があります。

デフォルトでは、カーソルは読み込み操作だけの宣言になります。 更新処理においても、カーソルを使用したい場合は、宣言コマンドに `FOR UPDATE` または `FOR DEFERRED`オプションを 指定する必要があります。 複数のカーソルを同じプログラム中で宣言することはできますが、 同じカーソルを何度も宣言することはできません。 同様に複数のカーソル名に対して同じコンテキストを定義することはできません。

カーソルを宣言した場合、そのカーソルを使用する前には カーソルのオープンをする必要があります。 カーソルは、`OPEN CURSOR`コマンドでオープンします。

カーソルをオープンするとことで、 カーソル宣言の`SELECT`ステートメントで定義した 検索処理が開始されます。 1 つのカーソルの宣言はプログラムでは 1 度しかできませんが、 オープンとクローズは何度でも実行することができます。 カーソルをオープンした後、 Empressは検索したコンテキストの最初の行を カレント行として準備しますが、単にカーソルをオープンしただけでは どのような行もカレント行にはなりません。

### 2.3.3 検索した行の処理

ユーザープログラムで定義したコンテキスト中の 次の有効な行を処理するためには、 必要なアトリビュート値を適切なプログラム変数に 読み込まれなければなりません。 この処理は、`FETCH`コマンドによって行い、 `FETCH`コマンドはコンテキスト中の次の有効な行を カレント行に設定し、指定されたアトリビュート値を それに対応するプログラム変数に読み込む試みをおこないます。

コンテキスト中の次に有効な行がロックされている場合、 (`SQLCODE = 1`)、 `FETCH`はそれをカレント行としますが、 指定した変数には適切な値が読み込まれません。 このような場合、`FETCH_AGAIN`コマンドを使用して、 これらの値を再検索をすることが可能です。 `FETCH_AGAIN`の操作は、`FETCH`と同じ処理を実行しますが、 次に有効な行ではなく、カレント行を読み込もうとする点が異なります。 すべての有効な行を読み込むには、ユーザープログラム中のループにおいて `SQLCODE = 100`になるまで`FETCH`する必要があります。

### 2.3.4 検索した行の更新および削除

検索したコンテキストにあるカレント行の更新および削除は、 `UPDATE` / `DELETE`コマンドを使用することができます。 これらのコマンドの形式では、`WHERE`句の代わりに `WHERE CURRENT OF`句を使用します。

この形式での`UPDATE`と`DELETE`コマンドは、 検索したコンテキスト中のカレントレコードに対して それぞれの処理を行います。 従って、`FETCH`コマンドによりカレント行の設定がされていない 場合は、これらのコマンドの処理は実行されません。

`WHERE CURRENT OF`句を指定して`UPDATE`を実行すると カーソルはその変更した行を指した状態のままになります。 （更新した行はカレント行のままです。） `WHERE CURRENT OF`句を指定して`DELETE`を実行すると カーソルはどの行も指していないことになります。 次の`FETCH`コマンドを実行するまで、カレントと認識される行 は存在しません。 次の`FETCH`コマンドを実行すると 次に有効な行をカレント行として設定します。

### 2.3.5 カーソルのクローズ

検索した行のコンテキストのカーソルによる処理を終了した場合、 カーソルをクローズしなくてはなりません。 カーソルは、`CLOSE CURSOR`コマンドによって 指定したカーソルをクローズします。

カーソルをクローズしても宣言自体は無効とはなりません。 その後にそのカーソルを再オープンして関連するコンテキストに対する初期化し、 利用することができます。 検索したコンテキストを初期化するには、前に説明した `OPEN CURSOR`コマンドを使用してカーソルを オープンする必要があります。

### 2.3.6 複数のカーソル

1 つのプログラム中で複数のカーソルを宣言することは可能です。 それぞれのカーソルは異なるコンテキストで検索するように定義することができ、 同じコンテキストは検索することは可能ですが、異なるカーソル名と 関連付けることはできません。

また、同時に複数のカーソルをオープンすることもできます。 しかし、この場合は最後にオープンしたカーソルだけは、 `WHERE CURRENT OF`句を指定した `FETCH`コマンド、 `UPDATE`および`DELETE`コマンド に対して関連付けられます。 先にオープンしたカーソルと関連付けようとすると、エラーメッセージが出力されます。 先にオープンしたカーソルをアクセスする場合、それ以降にオープンした すべてのカーソルを最初に`CLOSE CURSOR`コマンドでクローズしなくては なりません。

## 2.4 トランザクション

トランザクションは、`START TRANSACTION`コマンドで開始し、 `COMMIT TRANSACTION`あるいは`ROLLBACK TRANSACTION`コマンドで 終了する一連のコマンドで、トランザクション中においてのデータベースへの変更は、 そのトランザクションをコミットしない限り、一時的なものでしかありません。 トランザクションの処理中に実行した変更は、通常のコマンドでの処理結果と 変わりませんが、必要であれば、処理結果を変えるオプションもあります。

ディスク、オペーれティングシステム、ネットワークなどで障害が発生した場合、 トランザクションはウォームリスタート機能`empwarm`によって 解決(コミットまたはロールバック)されます。 これによってトランザクションに関係する すべてのデータベースのデータ整合性は保持されます。 ウォームリスタート機能は、 Empress: データベース管理者ガイド で説明されています。 トランザクションの全般的な紹介は Empress SQL: ユーザーガイドを参照してください。

## 2.5 プログラム変数の使用

プログラム変数は、SQL プリコンパラコマンドで使用することができます。 実行可能なコマンドの変数名は、名前の前にコロン(:)を付けなければなりません。 `FETCH`または`SELECT INTO`ステートメントによって取得した値を 格納するためにこれらの変数を使用することができます。 また、変数は、データベース、テーブル、アトリビュート、定数の名前を格納するために 使用することも可能です。

文字変数は、Empressデータは外部フォーマットとして扱われます。 （つまり文字列として） 文字変数を使用すると すべてのEmpressデータタイプを扱うことができます。

C 言語プログラムでは、文字変数は文字配列であるか、または 文字列へのポインタであり、これらを適切に宣言します。

数値変数の場合、 Empressデータは内部フォーマット(すなわちメモリに格納されている状態)で 扱われます。従って、 `SHORTINTEGER`、`INTEGER`、`LONGINTEGER`、 `FLOAT`、`LONGFLOAT`データタイプに対して、 ユーザープログラムから直接扱うことが可能となります。 加えて、外部フォーマットの文字変数と同じように 数値変数を扱うことが可能です。

以下に、 Empressデータタイプと C 言語の変数タイプの対応(マシンアーキテクチャに応じて 異なります。)について示します。

表 2-1 データタイプの対応

Empress | C
`SHORTINTEGER` | char
`INTEGER` | short/int (16 bits)
`LONGINTEGER` | int/long (32 bits)
`FLOAT` | float
`LONGFLOAT` | double
`CHAR, TEXT` | char */char []

### 2.5.1 変数の宣言

ユーザープログラムの特別なセクションにプログラム変数を定義しなければ、 これらの変数をプリコンパイラで使用することはできません。 このセクションは `BEGIN DECLARE SECTION`コマンドで始まり、 `END DECLARE SECTION`コマンドで終わる必要があります。

       EXEC SQL BEGIN DECLARE SECTION;
           int i;
           short j;
           float f;
           char str [10];
       EXEC SQL END DECLARE SECTION;

ホスト言語の構文に許されてるところであれば、宣言セクションはどの位置に 記述しても構いません。 また、1つのソースファイル中に複数の宣言セクションを記述しても構いません。 複数の宣言セクションを記述する場合、 すべての宣言セクションを通じて変数名はユニークでなければなりません。 宣言セクションは、SQL プリコンパイラステートメントを含むファイル内で グローバル(global)として宣言することを推奨します。

### 2.5.2 C 言語変数の宣言

プリコンパイラは`typedef`を含む、すべての C のタイプを認識します。 (その使用に先立って定義された適切なタイプを提供されている場合) 変数宣言を組み込むために適用される唯一の制限は、 多次元の配列はサポートされません。

プリコンパイラが受け入れる例として、プログラムの一部を 以下に示します。

       EXEC SQL BEGIN DECLARE SECTION
       ...
            typedef struct
                 {
                      long length;
                      float x[20];
                 } bulk;
            bulk b;
            long a[] = {10L, 15L, 3L, 123L, 1221L};
       ...
       EXEC SQL END DECLARE SECTION
       ...
            EXEC SQL INSERT INTO t (i, raw) VALUES (:a[k], :b);
       ...

テーブル"`t`"は、"`i`"と quot;`raw`"(バルクデータ) という 2つのアトリビュートを持つという前提で説明します。

ポインタと動的なメモリは、自由に使用することができ、 動的なオブジェクトは正常にアロケートされ利用することができます。 以下のプログラムの一部を検討します。

       EXEC SQL BEGIN DECLARE SECTION
       ...
            typedef struct
                 {
                      long length;
                      float x[20];
                 } bulk;
            bulk     *a, *b;
       ...
       EXEC SQL END DECLARE SECTION
       ...
            b = (bulk *) malloc (sizeof(bulk));
            a = (bulk *) malloc (sizeof(bulk));
       ...
       EXEC SQL INSERT INTO t (i, raw) VALUES (:a-> length, :b);

この場合では、ポインター使用の適用制限により、 データベース、テーブル、アトリビュートの名前にポインターを使用することが できません。

       EXEC SQL SELECT ... FROM :table ...;

この場合では 名前は文字型配列として宣言しなくてはなりません。 (`char *table` は受け付けません。)

また、 変数の参照をするための "`アドレス`" (`&`) オペレータは、使用することはできません。

### 2.5.3 シェル変数

環境変数は、すべての定数を受け付けるステートメント内か、あるいはパラメータの 変数として使用することができます。 変数を利用する場合、環境変数名のの前に"`$`"を付け、 二重引用符か単一引用符内で囲みます。例えば以下のように指定します。

       EXEC SQL SELECT amount FROM "$table"
            INTO :x WHERE id = "$name";

この例では、`table`と`name`は環境変数であり、 実行時に`table`および`name`のそれぞれに対し、 有効な値が環境変数に設定されている場合は置き換わります。 "`$`"記号付きの文字列を指定する場合は、"`$`" 記号を重ねて記述します。 例えば、"`$$alpha`"は、文字列"`$alpha`"を になりますが、これは単に文字列であり環境変数を利用する意味は全くありません。

### 2.5.4 制御変数

`FETCH`または`SELECT INTO`コマンドで検索し、 変数に格納したアトリビュート値が、`NULL`であるかを テストすることが可能です。 これを行うには、テストする各アトリビュートと制御変数を関連付ける 必要があります。

制御変数は次の形式で、内部では`long`型として扱います。

       :variable:control_variable

1つの制御変数で別々のステートメントの複数の変数の評価をするために することが可能です。 ただし、1つのステートメント内においては、 各プログラム変数に対して一意の制御変数を追加する必要があります。 追加しない場合は、制御変数は最後に処理したプログラム変数の値を 反映します。

### 2.5.5 フェッチと制御変数

`FETCH` または `SELECT INTO`コマンドにより 情報を検索する場合、 制御変数は以下のように評価されます。

`ctrl = 0` | 値は`NULL`ではなく、出力先の変数に正常に格納されたことを示します。
`ctrl < 0` | `NULL`値であることを示します。
`ctrl > 0` | 出力先の変数には値を格納するだけの十分な大きさがなく、 結果として値が切り詰められたことを示します。 この場合、制御変数には合いたいの実際の長さが格納され、 `SQLWARN0` と `SQLWARN1`指示子には 値として'`W`'が格納されます。

### 2.5.6 挿入と制御変数

制御変数を使用して、実行時に`NULL`を挿入(または `NULL`値への更新)ができます。 `INSERT`処理中には、コントロール変数は以下のように評価されます。

`ctrl < 0` | `NULL`が、行に挿入されます。
`ctrl = 0` | 変数の値が行にに挿入されます。

### 2.5.7 `WHERE`句と制御変数

制御変数を使用して、`WHERE`句の処理を変更できます。 実行ジに制御変数は次のように評価されます。

`ctrl < 0` | アトリビュートは`NULL`に対してテストされます。
`ctrl >= 0` | アトリビュートは変数の値に対してテストされます。

等しいか(`=`) 等しくないか(`!=`)をテストする `WHERE`句のみに制御変数を使用することができます。

## 2.6 SQL コミュニケーションエリア(SQLCA)

The SQLCA (SQL コミュニケーションエリア) は、 プリコンパイラとユーザーの両方に共通するデータ構造体です。 これは、エラーが発生した場合にエラーについての詳細な情報を 得るため、各ステートメントの終了ステータスを問い合わせる 手段として提供します。

### 2.6.1 SQLCA の使用

すべてのプログラムは、他のSQL ステートメントを記述する前に 以下のステートメントを記述する必要があります。

       EXEC SQL INCLUDE SQLCA;

各プリコンパラの操作の後、SQLCA は更新されます。 主な目的は、`SQLCODE`指示子を戻すことです。 `SQLCODE`の値は以下のような意味があります。

`SQLCODE = 0` | エラーは発生していません。
`SQLCODE < 0` | エラーは発生しました。
`SQLCODE = 100` | これ以上行は見つかりません。
`SQLCODE > 0` | 操作は正常に終了しましたが、例外が検出されました。

### 2.6.2 SQLCA 構造体

SQLCA データ構造体は以下になります。

`SQLCAID` | 文字列"SQLCA"を格納し、 構造体が最初に使用される時に更新されます。
`SQLABC` | SQLCA 構造体の長さを格納し、 構造体が最初に使用される時に更新されます。
`SQLERRML` | 実行時のエラーメッセージの長さを格納します。
`SQLERRMC` | エラーメッセージを格納します。
`SQLWARN` | 8 つの指示子で構成されるエリアで、各指示子は、 値として" " または "`W`"を格納します。 "`W`" は、例外状況が検出されたことを示し、 " "は、ステートメントが正常に実行されたことを示します。
`SQLWARN0` | この値が"`W`"である場合、 最後の操作中に他の指示子の1つに設定されます。
`SQLWARN1` | この値が"`W`"である場合、 最後の`FETCH` または `SELECT INTO` ステートメントの値は切り詰められます。

プログラム中からこれらの様々なエラー変数にアクセスすることができます。 これら以外の変数は SQL の互換および将来の使用のため予約されています。

C 言語でのSQLCA宣言の構造は以下のとおりです。

       struct sqlca{
          char sqlcaid[8];
          long sqlabc;
          long sqlcode;
          struct {
             unsigned short sqlerrml;
             char sqlerrmc[70];
             } sqlerrm;
          char sqlerrp[8];
          long sqlerrd[6];
          struct {
             char sqlwarn0;
             char sqlwarn1;
             char sqlwarn2;
             char sqlwarn3;
             char sqlwarn4;
             char sqlwarn5;
             char sqlwarn6;
             char sqlwarn7;
             } sqlwarn;
          char sqlext[8];
       } sqlca;

## 2.7 エラー処理

エラーおよび例外が発生した場合に実行すべき処理は `WHENEVER`コマンドに定義することができます。

`WHENEVER`コマンドを使用すると以下の状況での処置を定義することができます。

- エラーが検出された
- 警告は発せられた
- 行が返されないか見つからない

`WHENEVER`コマンドは、実行すべき処理を判断するために以下の `SQLCODE`の値を使用します。

`SQLCODE < 0` | `SQLERROR`のための指定した処理を実行します。
`SQLCODE = 100` | `NOT FOUND`のための指定した処理を実行します。
`SQLWARN0 =`'`W`' or (`SQLCODE > 0` and `SQLCODE != 100`) | `SQLWARNING`のための指定した処理を実行します。

## 2.8 制限

Empress SQLプリコンパイラのためのプログラミングを行う場合、 以下に示す規則に従う必要があります。

1. 埋め込み SQL ステートメント内に C マクロを使うことはできません。

2. 埋め込み SQL を含むすべてのファイルにインクルードファイルとして `#include <mscc.h>`を指定する必要があります。

3. プログラムではデータベースとテーブルの参照は一貫している必要があります。 例えば、データベース指定をしたテーブル名を使いテーブルを オープンした場合、このプログラムでテーブルを参照するときには すべてデータベースの指定も含めなければなりません。

4. プログラムを起動した時点では、デフォルトのデータベースは存在しません。 デフォルトデータベースを設定するために`DATABASE IS`コマンドを 発行しなければなりません。

5. 複数の宣言セクションにわたる変数名は、コンパイル単位(ソースファイル単位)において ユニークでなければなりません。

6. `CHAR`タイプの配列は多次元配列を記述することはできません。

7. "`EXEC SQL INCLUDE SQLCA`" ステートメントは、 他の SQL ステートメントよりも前に記述しなくてはなりません。

8. "`EXEC SQL INIT`"ステートメントは最初の実行可能SQL ステートメント でなければなりません。 最初に記述しない場合、 ランタイムエラーおよび予測不可能な結果になります。

9. "`EXEC SQL INIT`"ステートメントは、2度以上コールすることは できません。 それぞれの呼び出しの後のエラーは`SQLCODE`にマイナス値によって示されます。

## 2.9 プリコンパイラの実行

Empress SQLプリコンパイラを使用することで、 C プログラム中から SQL コマンドを発行することが可能です。 プリコンパイルの実行により、プログラムに埋め込れた SQL コマンドは それを実行する関数のコールに置き換えられます。

SQL コマンドが埋め込まれた C プログラムは、 以下のコマンドを使ってプリコンパイルすることができます。 （基本的な使用法のみ記載)

       empesql infile.c [outfile.c]

`infile.c` | SQL ステートメントが埋め込まれた プログラムファイル。
`outfile.c` | 関数のコールに置き換えられたSQLコマンドを含むソースファイル。 `outfile`が指定されていない場合、デフォルト名は`mpout.c`が 使用されます。

以下のコマンドの手順を実行することで SQL ステートメントが埋め込まれた C プログラムをプリコンパイル、コンパイル、リンクができます。 ("`out`"は、実行ファイル名です。)

       empesql infile.c outfile.c
       empecc -o out outfile.c

現在のところ、各入力ファイルを分割してプリコンパイルを実行する 必要がありますが、C プリコンパイラによって出力された複数のファイルを 設定することで1つの実行プログラムをコンパイルしリンクすることができます。