2 プリコンパイラの概要
この章では埋込みSQLプログラムの動作を説明します。それらが動作する特殊な環境と、その環境がアプリケーション設計に及ぼす影響について調べます。埋込みSQLプログラミングの基本概念とアプリケーション開発の手順について説明した後、簡単なプログラムを使用して、要点を具体的に説明します。
この章のトピックは、次のとおりです:
2.1 埋込みSQLプログラミングの基本概念
この項では、後に続く章の内容の基本概念について説明します。この項の内容は次のとおりです。
2.1.1 埋込みSQL文
埋込みSQLとは、アプリケーション・プログラムに記述されているSQL文のことです。SQL文を含むアプリケーション・プログラムはホスト・プログラムと呼ばれ、その記述言語はホスト言語と呼ばれます。たとえば、Pro*C/C++では、特定のSQL文をCまたはC++ホスト・プログラムに埋め込むことができます。
Oracleデータの操作および問合せには、INSERT文、UPDATE文、DELETE文およびSELECT文を使用します。INSERTではデータの行をデータベース表に追加し、UPDATEでは行を変更し、DELETEでは不要な行を削除し、SELECTでは検索条件と一致する行を取り出します。
強力なSET ROLE文を使用すると、データベース権限を動的に管理できます。ロールとは、関連するシステム権限やオブジェクト権限の名前付きグループ、あるいはユーザーまたは他のロールに付与された関連するシステム権限やオブジェクト権限の名前付きグループです。ロールの定義は、Oracleデータ・ディクショナリに格納されます。アプリケーションでは、必要に応じてSET ROLE文を使用し、ロールを有効または無効にできます。
アプリケーション・プログラムではSQL文のみ有効であり、SQL*Plus文は無効です。(SQL*Plusにはレポートの書式化、SQL文の編集、環境パラメータの設定のための文が追加されています。)
2.1.1.1 実行文とディレクティブ
埋込みSQL文には、すべての対話型SQL文に加えて、Oracleとホスト・プログラムの間でデータを転送できるその他の文があります。埋込みSQL文には、実行文およびディレクティブという2つのタイプがあります。実行文では、SQLLIBランタイム・ライブラリへのコールが発生します。実行文は、Oracleへの接続、Oracleデータの定義、問合せ、操作、Oracleデータへのアクセス制御およびトランザクション処理に使用します。CまたはC++言語の実行文を配置できる位置であれば、任意の位置に記述できます。
一方、ディレクティブではSQLLIBへのコールは発生せず、Oracleデータの操作も行われません。宣言文は、Oracleオブジェクト、通信領域およびSQL変数を宣言するために使用します。CまたはC++の変数宣言を配置できる位置であれば、任意の位置に記述できます。
表2-1は、各種の埋込みSQL文の一部をグループ化したものです。
表2-1 埋込みSQL文
デイレクティブ | 用途 |
---|---|
ARRAYLEN* |
PL/SQLでのホスト配列の使用 |
BEGIN DECLARE SECTION* END DECLARE SECTION* |
ホスト変数の宣言(オプション) |
DECLARE* |
Oracleスキーマ・オブジェクトの命名 |
INCLUDE* |
ファイルへのコピー |
TYPE* |
データ型の同値化 |
VAR* |
変数の同値化 |
WHENEVER* |
ランタイム・エラーの処理 |
表2-2 埋込みSQL文
実行文 | 用途 |
---|---|
ALLOCATE* |
Oracleデータの定義および制御 |
ALTER |
- |
ANALYZE |
- |
DELETE |
DML |
INSERT |
- |
SELECT |
- |
UPDATE |
- |
COMMIT |
トランザクションの処理 |
ROLLBACK |
- |
SAVEPOINT |
- |
SET TRANSACTION |
- |
DESCRIBE* |
動的SQLの使用 |
EXECUTE* |
- |
PREPARE* |
- |
ALTER SESSION |
セッションの制御 |
SET ROLE |
- |
*対話形式はありません。 |
2.1.2 埋込みSQL構文
作成したアプリケーション・プログラムでは、完全なSQL文と完全なCプログラムを自由に混在させ、SQL文にC言語の変数や構造体を使用できます。SQL文をホスト・プログラム内に作成する場合の唯一の特殊要件は、SQL文をEXEC SQLキーワードで開始し、セミコロンで終了することです。Pro*C/C++では、すべてのEXEC SQL文がSQLLIBランタイム・ライブラリへのコールに変換されます。
多くの埋込みSQL文は、新しい句が追加される点、またはプログラム変数が使用される点のみが同等の対話型SQLと異なります。次の例は、対話型ROLLBACK文と埋込みROLLBACK文の対比を示しています。
ROLLBACK WORK: -- interactive EXEC SQL ROLLBACK WORK; -- embedded
この2つの文の効果は同じですが、対話型SQL環境(SQL*Plusの実行時など)では前者を、Pro*C/C++プログラムでは後者を使用します。
2.1.3 静的SQL文と動的SQL文
ほとんどのアプリケーション・プログラムは、静的SQL文と固定的なトランザクションを処理するように設計されています。この場合、実行前にそれぞれのSQL文およびトランザクションの構成が認識できます。つまり、どのSQLコマンドが発行され、どのデータベースの表が変更され、どの列が更新されるかなどが事前にわかっています。
ただし、アプリケーションによっては、任意の有効なSQL文を実行時に受け入れて処理することを要求される場合もあります。したがって、関係するSQLコマンド、データベース表および列が実行時までわからないことがあります。
動的SQLは、実行時のプログラムでSQL文を受け入れるか作成し、データ型の変換を明示的に制御する高度なプログラミング技術です。
2.1.4 埋込みPL/SQLブロック
Pro*C/C++では、PL/SQLブロックが1つの埋込みSQL文と同様に扱われます。PL/SQLブロックは、アプリケーション・プログラム内でSQL文を記述できる位置であれば、任意の位置に記述できます。PL/SQLをホスト・プログラムに埋め込むには、単にPL/SQLと共有する変数を宣言し、PL/SQLブロックをEXEC SQL EXECUTEおよびEND-EXECキーワードで囲みます。
PL/SQLはすべてのSQLデータ操作コマンドおよびトランザクション処理コマンドをサポートしているため、埋込みPL/SQLブロックからOracleデータを柔軟かつ安全に操作できます。
2.1.5 ホスト変数および標識変数
ホスト変数は、Oracleとプログラムとの間で通信を行うための鍵です。ホスト変数とは、C言語で宣言され、Oracleで共有される(つまり、プログラムとOracleの両方がその値を参照できる)スカラー変数または集合体変数です。
プログラムでは、入力ホスト変数を使用してOracleにデータを渡します。Oracleでは、出力ホスト変数を使用してプログラムにデータおよびステータス情報を渡します。プログラムは入力ホスト変数に値を割り当て、Oracleは出力ホスト変数に値を割り当てます。
ホスト変数は、SQL式を使用できる位置であれば任意の位置に使用できます。SQL文では、SQLキーワードと区別するために、ホスト変数に接頭辞としてコロン(:)を付ける必要があります。
C構造体を使用して、複数のホスト変数を含めることもできます。接頭辞コロンを付けて埋込みSQL文の構造体を命名すると、Oracleでは構造体の各コンポーネントがホスト変数として使用されます。
任意のホスト変数に任意指定の標識変数を関連付けることができます。標識変数は、関連付けられたホスト変数の値または条件を示すshort int型変数です。標識変数は、入力ホスト変数へのNULLの割当てと、出力ホスト変数に含まれるNULLまたは切捨て値の検出に使用されます。NULL値は、欠落している値、不明な値または適用不能な値です。
SQL文の場合、標識変数には接頭辞コロンを付けて、対応するホスト変数の直後に記述する必要があります。さらにわかりやすくするには、ホスト変数とその標識変数の間にINDICATORキーワードを記述します。
ホスト変数が構造体にパッケージされていて、標識変数を使用する場合は、ホスト構造体の各ホスト変数に対する標識変数を含む構造体を作成し、SQL文でインジケータ構造体を命名します。インジケータ構造体の名前の前にはコロンを付け、ホスト変数の構造体の直後に指定します。また、INDICATORキーワードを使用して、ホスト構造体とそれに対応するインジケータ構造体を分離することもできます。
2.1.6 Oracleのデータ型
通常、ホスト・プログラムからOracleにデータが入力され、Oracleからプログラムにデータが出力されます。Oracleではデータベース表に入力データが格納され、出力データはプログラム・ホスト変数に格納されます。データ項目を格納するために、Oracleではそのデータ型を認識する必要があり、データ型により値の記憶形式と有効範囲が指定されます。
Oracleでは、内部データ型と外部データ型という2種類のデータ型が認識されます。内部データ型は、Oracleでデータベース列にデータを格納する方法を指定します。また、Oracleでは、データベース疑似列を表す内部データ型も使用されます。データベース疑似列は特定のデータ項目を戻すものの、表には実際の列はありません。
外部データ型は、データがホスト変数にどのように格納されるかを指定します。ホスト・プログラムがOracleにデータを入力すると、Oracleは必要に応じて、入力ホスト変数の外部データ型とターゲット・データベース列の内部データ型の間で変換を行います。Oracleがホスト・プログラムにデータを出力すると、Oracleは必要に応じて、ソース・データベース列の内部データ型と出力ホスト変数の外部データ型の間で変換を行います。
2.1.9 プライベートSQL領域、カーソルおよびアクティブ・セット
Oracleでは、SQL文を処理するためにプライベートSQL領域と呼ばれる作業領域がオープンされます。このプライベートSQL領域にはSQL文の実行に必要な情報が格納されます。カーソルと呼ばれる識別子を使用すると、SQL文に名前を付け、そのプライベート領域内の情報にアクセスし、その処理をある程度制御できます。
静的SQL文には、明示的および暗黙的という2種類のカーソルがあります。Oracleでは、1行のみを戻すSELECT文(問合せ)などのすべてのデータ定義文とDML文に対しては、1つのカーソルが暗黙的に宣言されます。ただし、複数行を戻す問合せで2行目以降を処理する場合は、カーソルを明示的に宣言(またはホスト配列を使用)する必要があります。
戻された一連の行はアクティブ・セットと呼ばれ、そのサイズは問合せの検索条件と何行一致するかによって異なります。現在処理している行(カレント行と呼ばれる)を識別するには、明示カーソルを使用します。
たとえば、端末の画面に一連の行が戻されたとします。画面上のカーソルは、最初に処理される行、次に処理される行というように移動していきます。同様に、明示カーソルはアクティブ・セット内の現在行を指します。これを利用して、プログラムは行を1行ずつ処理できます。
2.1.10 トランザクション
トランザクションとは、論理的に関連のある一連のSQL文です(ある銀行勘定の貸方に記帳し、別の銀行勘定の借方に記帳する2つのUPDATEなど)。Oracleでは、トランザクションは1単位として扱われるため、それぞれの文による変更はすべてが同時に確定されるか、取り消されるかします。
最後のデータ定義、COMMITまたはROLLBACK文が実行された後に実行するDML文すべてが、現行のトランザクションを構成します。
データベースの整合性を維持するために、Pro*C/C++ではCOMMIT文、ROLLBACK文およびSAVEPOINT文を使用してトランザクションを定義できます。
COMMITでは、現行のトランザクション中にデータベースに加えられた変更が確定されます。ROLLBACKでは、現行のトランザクションを終了し、トランザクションの開始以降に加えられた変更がすべて取り消されます。SAVEPOINTでは、トランザクション処理の現在の位置にマークが付けられ、ROLLBACKと併用することで、トランザクションを部分的に取り消すことができます。
2.1.11 エラーおよび警告
埋込みSQL文を実行すると、エラーまたは警告が発生する場合があります。これらの結果を処理する方法が必要です。Pro*C/C++には、SQL通信領域(SQLCA)とWHENEVER文という2つのエラー処理方法が用意されています。
SQLCAは、ホスト・プログラムに組み込む(ハードコードする)データ構造体です。Oracleにより使用されるプログラム変数を定義して、ランタイム・ステータス情報をプログラムに渡します。SQLCAを使用すると、直前に試みた処理に関するOracleからのフィードバックに基づいて、異なる処理を実行できます。たとえば、DELETE文が成功したかチェックし、成功した場合は削除された行数をチェックできます。
WHENEVER文を使用すると、Oracleがエラーまたは警告状態を検出した際に自動的に行われるアクションを指定できます。これらの処理は、次の文の継続実行、関数のコール、ラベル付き文への分岐、停止です。
2.2 埋込みSQLアプリケーションの開発ステップ
図2-1は、埋込みSQLアプリケーションの開発プロセスを示しています。
図のように、プリコンパイルの結果、通常のコンパイルが可能な変更済ソース・ファイルが生成されます。従来の開発プロセスにプリコンパイル処理が追加されますが、このステップによりきわめて柔軟なアプリケーションを作成できます。
2.3 プログラミングのガイドライン
2.3.1 コメント
SQL文には、空白を挿入できる位置(EXEC SQLキーワードの間以外)であれば、任意の位置にC言語の形式のコメント(/* ... */)を記述できます。また、SQL文の行末には、次の例のようにANSI形式のコメント(-- ...)も挿入できます。
EXEC SQL SELECT ENAME, SAL INTO :emp_name, :salary -- output host variables FROM EMP WHERE DEPTNO = :dept_number;
CODE=CPPプリコンパイラ・オプションを使用してプリコンパイルする場合は、Pro*C/C++ソース内でC++形式のコメント(//)を使用できます。
2.3.3 宣言部
宣言部にはホスト変数宣言が含まれており、その形式は次のとおりです。
EXEC SQL BEGIN DECLARE SECTION; /* Declare all host variables inside this section: */ char *uid = "username/password"; ... EXEC SQL END DECLARE SECTION;
EXEC SQL BEGIN DECLARE SECTION;
次の文で終了します。
EXEC SQL END DECLARE SECTION;
-
ホスト変数および標識変数の宣言
-
非ホストC/C++変数
-
EXEC SQL DECLARE文
-
EXEC SQL INCLUDE文
-
EXEC SQL VAR文
-
EXEC SQL TYPE文
-
EXEC ORACLE文
-
C/C++コメント
宣言部が必要になるのは、MODE=ANSIまたはCODE=CPP(C++アプリケーション内)、PARSE=NONEまたはPARTIALの場合です。
宣言部は複数使用できます。異なるコード・モジュールでもかまいません。
関連項目
2.3.4 デリミタ
Cでは1文字を区切る場合、一重引用符を次のように使用します。
ch = getchar(); switch (ch) { case 'U': update(); break; case 'I': insert(); break; ...
SQLでは文字列を区切る場合、一重引用符を次のように使用します。
EXEC SQL SELECT ENAME, SAL FROM EMP WHERE JOB = 'MANAGER';
C言語では文字列を区切る場合、二重引用符を次のように使用します。
printf("\nG'Day, mate!");
SQLでは特殊文字または小文字を含む識別子を区切る場合、二重引用符を次のように使用します。
EXEC SQL CREATE TABLE "Emp2" (empno number(4), ...);
2.3.5 ファイルの長さ
Pro*C/C++で処理できるソース・ファイルの長さには制限があります。許容される行数には制限があります。ファイル・サイズの制約となる要因には、ソース・ファイルの次のような側面があります。
-
埋込みSQL文の複雑さ(バインド変数と定義変数の数など)
-
データベース名の使用の有無(AT句を使用してデータベースに接続するなど)
-
埋込みSQL文の数
この制限に関連した問題を防ぐには、複数のプログラム単位を使用してソース・ファイルのサイズを小さくします。
2.3.6 関数プロトタイプ
ANSI C規格(X3.159-1989)は、関数プロトタイプを提供しています。関数プロトタイプでは、関数およびその引数のデータ型を宣言しており、Cコンパイラでは欠落している引数や一致しない引数を検出できます。
CODEオプションにより、プリコンパイラでCまたはC++コードが生成される方法が決まります。このオプションは、コマンドラインまたは構成ファイルに入力できます。
2.3.6.1 ANSI_C
CODE=ANSI_Cを指定してプログラムをプリコンパイルすると、プリコンパイラにより完全にプロトタイプ化された関数宣言が生成されます。次に例を示します。
extern void sqlora(long *, void *);
2.3.6.2 KR_C
CODE=KR_C(KRは「Kernighan and Ritchie」を表します)オプションを指定してプリコンパイルすると、関数のパラメータ・リストがコメント・アウトされていることを除き、ANSI_Cを指定した場合と同じように関数プロトタイプが生成されます。次に例を示します。
extern void sqlora(/*_ long *, void * _*/);
したがって、ANSI CがサポートされていないCコンパイラを使用する場合は、必ずプリコンパイラ・オプションCODEをKR_Cに設定してください。CODEオプションをANSI_Cに設定すると、プリコンパイラではconst型の修飾子など、他のANSI固有の構造体も生成できます。
2.3.8 ホスト変数名
ホスト変数名には、大文字または小文字、数字およびアンダースコアを使用できますが、最初の文字は英字にする必要があります。長さは任意ですが、Pro*C/C++にとって重要なのは最初の31文字のみです。Cコンパイラやリンカーによっては最大長が短い場合があるため、使用するCコンパイラのユーザーズ・ガイドで確認してください。
移植性を考慮する場合は、ホスト変数名を18文字以下(SQL標準での長さの要件)に制限することもできます。
関連項目
2.3.9 行の継続
SQL文は、ある行から次の行に続けることができます。文字列リテラルをある行から次の行に続ける場合は、次のようにバックスラッシュ(\)を使用する必要があります。
EXEC SQL INSERT INTO dept (deptno, dname) VALUES (50, 'PURCHAS\ ING');
このコンテキストでは、バックスラッシュはプリコンパイラで継続文字として扱われます。
2.4 条件付きプリコンパイル
条件付きプリコンパイルとは、特定の条件に基づいてコード・セクションをホスト・プログラムに組み込む(または除外する)プリコンパイルの方法です。たとえば、UNIXでプリコンパイルするときにはあるコード・セクションを組み込み、VMSでプリコンパイルするときには別のコード・セクションを組み込むことができます。条件付きプリコンパイルの使用により、様々な環境で実行できるプログラムを作成できます。
環境および処理を定義する文によってコードの条件文が区切られます。これらのセクションには、CまたはC++の文とEXEC SQL文を記述できます。次の文でプリコンパイルの条件を制御します。
EXEC ORACLE DEFINE symbol; -- define a symbol EXEC ORACLE IFDEF symbol; -- if symbol is defined EXEC ORACLE IFNDEF symbol; -- if symbol is not defined EXEC ORACLE ELSE; -- otherwise EXEC ORACLE ENDIF; -- end this control block
すべてのEXEC ORACLE文の終わりには、セミコロンを付ける必要があります。
2.4.1 記号の定義
シンボルを定義するには2通りの方法があります。1つの方法では、ホスト・プログラムに次の文を含めます。
EXEC ORACLE DEFINE symbol;
もう1つの方法では、次の構文を使用してコマンドラインで記号を定義します。
... DEFINE=symbol ...
symbol
には、大/小文字区別がありません。
注意:
#defineプリプロセッサ・ディレクティブは、EXEC ORACLE DEFINE文とは異なります。
Pro*C/C++をシステムにインストールするときに、ポート固有の記号がいくつか事前定義されます。たとえば、オペレーティング・システムの事前定義済記号には、CMS、MVS、MS-DOS、UNIXおよびVMSがあります。
2.5 分割プリコンパイル
複数のCまたはC++プログラム・モジュールを別々にプリコンパイルし、それらをリンクして1つの実行可能プログラムにできます。これにより、プログラムの機能コンポーネントの作成とデバッグを複数のプログラマが分担して行う場合に必要とされる、モジュラー・プログラミングが可能になります。個々のプログラム・モジュールを同じ言語で作成する必要はありません。
2.5.1 ガイドライン
2.5.1.3 単一のSQLCAの使用
使用するSQLCAが1つのみの場合は、1つのプログラム・モジュール内でglobalとして宣言し、他のモジュール内ではexternalとして宣言する必要があります。extern記憶域クラスを使用して、コードに次の定義を追加します。
#define SQLCA_STORAGE_CLASS extern
この例は、プリコンパイラに対して、他のプログラム・モジュール内でSQLCAを検索するように指示しています。SQLCAをexternalとして宣言しないかぎり、各プログラム・モジュールでは専用のローカルSQLCAが使用されます。
注意:
アプリケーションのソース・ファイルはすべて、名前が一意である必要があります。一意でないとエラーが発生します。
2.6 コンパイルおよびリンク
実行可能プログラムを作成するには、プリコンパイラにより生成された出力である.c
ソース・ファイルをコンパイルし、生成されるオブジェクト・モジュールをSQLLIBおよびシステム固有のOracleライブラリに必要なモジュールとリンクさせる必要があります。プリコンパイラ・コードとOCIコールを併用している場合は、OCIランタイム・ライブラリ(UNIXシステムではliboci.a
)にもリンクさせてください。
リンカーはオブジェクト・モジュール内のシンボリック参照を解決します。これらの参照で競合が発生すると、リンクは失敗します。このような失敗が起こるのは、サード・パーティ・ソフトウェアをプリコンパイル済プログラムにリンクする場合などです。すべてのサード・パーティ・ソフトウェアにOracleとの互換性があるとはかぎりません。したがって、プログラムをリンクさせて共有にすると、原因不明のエラーが発生することがあります。スタンドアロン型や2タスク型でリンクさせると、問題が解消する場合があります。
コンパイルとリンクはシステムに依存します。ほとんどのプラットフォームでは、Pro*C/C++アプリケーションのプリコンパイル、コンパイルおよびリンクに使用できるように、サンプルのMakeファイルまたはバッチ・ファイルが用意されています。システム固有のマニュアルを参照してください。
2.7 例の表
このマニュアルのほとんどのプログラミング例では、DEPTおよびEMPサンプル・データベース表を使用しています。これらの表の定義を次に示します。
CREATE TABLE DEPT (DEPTNO NUMBER(2) NOT NULL, DNAME VARCHAR2(14), LOC VARCHAR2(13)) CREATE TABLE EMP (EMPNO NUMBER(4) NOT NULL, ENAME VARCHAR2(10), JOB VARCHAR2(9), MGR NUMBER(4), HIREDATE DATE, SAL NUMBER(7,2), COMM NUMBER(7,2), DEPTNO NUMBER(2))
2.7.1 サンプル・データ
DEPT表とEMP表には、それぞれ次のデータ行が含まれて
います。
DEPTNO DNAME LOC ------- ---------- --------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ----- ------- --------- ------ --------- ------ ------ ------- 7369 SMITH CLERK 7902 17-DEC-80 800 20 7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30 7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30 7566 JONES MANAGER 7839 02-APR-81 2975 20 7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30 7698 BLAKE MANAGER 7839 01-MAY-81 2850 30 7782 CLARK MANAGER 7839 09-JUN-81 2450 10 7788 SCOTT ANALYST 7566 19-APR-87 3000 20 7839 KING PRESIDENT 17-NOV-81 5000 10 7844 TURNER SALESMAN 7698 08-SEP-81 1500 30 7876 ADAMS CLERK 7788 23-MAY-87 1100 20 7900 JAMES CLERK 7698 03-DEC-81 950 30 7902 FORD ANALYST 7566 03-DEC-81 3000 20 7934 MILLER CLERK 7782 23-JAN-82 1300 10
2.8 サンプル・プログラム: 単純な問合せ
Pro*C/C++および埋込みSQLをよく理解する方法の1つは、サンプル・プログラムを学習することです。次のプログラムは、Pro*C/C++のdemo
ディレクトリにあるsample1.pc
ファイルにあり、オンラインでも使用可能です。
このプログラムはOracleに接続した後でループし、従業員番号の入力を求めるプロンプトを表示します。データベースに従業員名、給与およびコミッションを問い合せて、その情報を表示した後にループを継続します。この情報はホスト構造体に戻されます。また、SELECTで選択された出力値にNULLが含まれるかどうかを示すパラレル・インジケータ構造体もあります。
MODE=ORACLEプリコンパイラ・オプションを使用して、サンプル・プログラムをプリコンパイルします。
注意:
この機能を簡単に説明するために、この例では、デプロイされたシステムで通常使用されるパスワード管理手法を実行していません。本番環境では、Oracle Databaseのパスワード管理ガイドラインに従い、サンプル・アカウントを無効にしてください。パスワード管理ガイドラインおよびその他のセキュリティ上の推奨事項については、『Oracle Databaseセキュリティ・ガイド』を参照してください。
/* * sample1.pc * * Prompts the user for an employee number, * then queries the emp table for the employee's * name, salary and commission. Uses indicator * variables (in an indicator struct) to determine * if the commission is NULL. * */ #include <stdio.h> #include <string.h> /* Define constants for VARCHAR lengths. */ #define UNAME_LEN 20 #define PWD_LEN 40 /* Declare variables. No declare section is needed if MODE=ORACLE.*/ VARCHAR username[UNAME_LEN]; /* VARCHAR is an Oracle-supplied struct */ varchar password[PWD_LEN]; /* varchar can be in lower case also. */ /* Define a host structure for the output values of a SELECT statement. */ struct { VARCHAR emp_name[UNAME_LEN]; float salary; float commission; } emprec; /* Define an indicator struct to correspond to the host output struct. */ struct { short emp_name_ind; short sal_ind; short comm_ind; } emprec_ind; /* Input host variable. */ int emp_number; int total_queried; /* Include the SQL Communications Area. You can use #include or EXEC SQL INCLUDE. */ #include <sqlca.h> /* Declare error handling function. */ void sql_error(); main() { char temp_char[32]; /* Connect to ORACLE-- * Copy the username into the VARCHAR. */ strncpy((char *) username.arr, "SCOTT", UNAME_LEN); /* Set the length component of the VARCHAR. */ username.len = strlen((char *) username.arr); /* Copy the password. */ strncpy((char *) password.arr, "TIGER", PWD_LEN); password.len = strlen((char *) password.arr); /* Register sql_error() as the error handler. */ EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n"); /* Connect to ORACLE. Program will call sql_error() * if an error occurs when connecting to the default database. */ EXEC SQL CONNECT :username IDENTIFIED BY :password; printf("\nConnected to ORACLE as user: %s\n", username.arr); /* Loop, selecting individual employee's results */ total_queried = 0; for (;;) { /* Break out of the inner loop when a * 1403 ("No data found") condition occurs. */ EXEC SQL WHENEVER NOT FOUND DO break; for (;;) { emp_number = 0; printf("\nEnter employee number (0 to quit): "); gets(temp_char); emp_number = atoi(temp_char); if (emp_number == 0) break; EXEC SQL SELECT ename, sal, NVL(comm, 0) INTO :emprec INDICATOR :emprec_ind FROM EMP WHERE EMPNO = :emp_number; /* Print data. */ printf("\n\nEmployee\tSalary\t\tCommission\n"); printf("--------\t------\t\t----------\n"); /* Null-terminate the output string data. */ emprec.emp_name.arr[emprec.emp_name.len] = '\0'; printf("%-8s\t%6.2f\t\t", emprec.emp_name.arr, emprec.salary); if (emprec_ind.comm_ind == -1) printf("NULL\n"); else printf("%6.2f\n", emprec.commission); total_queried++; } /* end inner for (;;) */ if (emp_number == 0) break; printf("\nNot a valid employee number - try again.\n"); } /* end outer for(;;) */ printf("\n\nTotal rows returned was %d.\n", total_queried); printf("\nG'day.\n\n\n"); /* Disconnect from ORACLE. */ EXEC SQL COMMIT WORK RELEASE; exit(0); } void sql_error(msg) char *msg; { char err_msg[128]; int buf_len, msg_len; EXEC SQL WHENEVER SQLERROR CONTINUE; printf("\n%s\n", msg); buf_len = sizeof (err_msg); sqlglm(err_msg, &buf_len, &msg_len); if (msg_len > buf_len) msg_len = buf_len; printf("%.*s\n", msg_len, err_msg); EXEC SQL ROLLBACK RELEASE; exit(1); }
2.9 サンプル・プログラム: SQL99構文を使用する単純な問合せ
このプログラムは前述のサンプルと似ていますが、SELECT、INSERT、DELETEおよびUPDATE文のSQL99構文を使用し、DECLARE CURSOR文のカーソル本体がサポートされています。
MODE=ORACLEプリコンパイラ・オプションを使用して、サンプル・プログラムをプリコンパイルします。
/*
* sql99.pc
*
* Prompts the user for an employee number,
* then queries the emp table for the employee's
* name, salary and department.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlda.h>
#include <sqlcpr.h>
.
/* Define constants for VARCHAR lengths. */
#define UNAME_LEN 30
#define PWD_LEN 40
/* Declare variables. No declare section is needed if MODE=ORACLE. */
VARCHAR username[UNAME_LEN];
/* VARCHAR is an Oracle-supplied struct */
varchar password[PWD_LEN];
/* varchar can be in lower case also. */
/* Define a host structure for the output values of a SELECT statement. */
struct{
VARCHAR emp_name[UNAME_LEN];
float salary;
VARCHAR dept_name[UNAME_LEN] ;
} emprec;
/* Define an indicator struct to correspond to the host output struct. */
struct{
short emp_name_ind;
short sal_ind;
short dept_name;
} emprec_ind;
/* Input host variable. */
int emp_number;
int total_queried;
/* Include the SQL Communications Area. You can use #include or EXEC SQL
INCLUDE. */
#include <sqlca.h>
/* Declare error handling function. */
void sql_error(msg)
char *msg;
{
char err_msg[128];
size_t buf_len, msg_len;
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s\n", msg);
buf_len = sizeof (err_msg);
sqlglm(err_msg, &buf_len, &msg_len);
printf("%.*s\n", msg_len, err_msg);
EXEC SQL ROLLBACK RELEASE;
exit(EXIT_FAILURE);
}
void main(){
char temp_char[32];
/* Connect to ORACLE-- * Copy the username into the VARCHAR. */
strncpy((char *) username.arr, "scott", UNAME_LEN);
/* Set the length component of the VARCHAR. */
username.len = (unsigned short) strlen((char *) username.arr);
/* Copy the password. */
strncpy((char *) password.arr, "tiger", PWD_LEN);
password.len = (unsigned short) strlen((char *) password.arr);
/* Register sql_error() as the error handler. */
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");
/* Connect to ORACLE. Program will call sql_error() * if an error occurs
when connecting to the default database. */
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnected to ORACLE as user: %s\n", username.arr);
/* Loop, selecting individual employee's results */
total_queried = 0;
for (;;) {
emp_number = 0;
printf("\nEnter employee number (0 to quit): ");
gets(temp_char);
emp_number = atoi(temp_char);
if (emp_number == 0)
break;
/* Branch to the notfound label when the * 1403 ("No data found") condition
occurs. */
EXEC SQL WHENEVER NOT FOUND GOTO notfound;
/* The following query uses SQL99 syntax - RIGHT OUTER JOIN */
EXEC SQL SELECT e.ename, e.sal, d.dname
INTO :emprec INDICATOR :emprec_ind
FROM EMP e RIGHT OUTER JOIN dept d
ON e.deptno = d.deptno
WHERE e.EMPNO = :emp_number;
/* Print data. */
printf("\n\nEmployee Salary Department Name\n");
printf("-------- ------- ------------------\n");
/* Null-terminate the output string data. */
emprec.emp_name.arr[emprec.emp_name.len] = '\0';
emprec.dept_name.arr[emprec.dept_name.len]='\0';
printf("%s %7.2f %s ", emprec.emp_name.arr,
emprec.salary, emprec.dept_name.arr);
total_queried++;
continue;
notfound:
printf("\nNot a valid employee number - try again.\n");
}
printf("\n\nTotal rows returned was %d.\n", total_queried);
printf("\nG'day.\n\n\n");
/* Disconnect from ORACLE. */
EXEC SQL ROLLBACK WORK RELEASE;
exit(EXIT_SUCCESS);
}