日本語PDF

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.7 配列

Pro*C/C++では、配列ホスト変数(ホスト配列と呼ばれます)および構造体の配列を定義して、1つのSQL文で操作できます。配列に対するSELECT、FETCH、DELETE、INSERTおよびUPDATE文を使用すると、大量のデータを容易に問合せおよび操作できます。また、ホスト配列をホスト変数の構造体の中で使用することもできます。

2.1.8 データ型の同値化

Pro*C/C++ではデータ型を同値化できるため、アプリケーションの柔軟性が向上します。つまり、Oracleが入力データを解釈し、出力データをフォーマットする方法をカスタマイズできます。

変数ごとに、サポートされているC言語のデータ型を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.1.12 SQL99構文サポート

SQL規格により、規格に準拠するすべてのソフトウェア製品で、SQLアプリケーションを移植できます。Oracle機能は、ANSI/ISO SQL99規格(ANSI準拠の結合を含む)に準拠しています。Pro*C/C++では、OracleデータベースでサポートされているすべてのSQL99機能がサポートされており、SELECT文、INSERT文、DELETE文およびUPDATE文と、DECLARE CURSOR文でのカーソル本体のSQL99構文がサポートされています。

2.2 埋込みSQLアプリケーションの開発ステップ

図2-1は、埋込みSQLアプリケーションの開発プロセスを示しています。

図2-1 埋込みSQLアプリケーションの開発プロセス

図2-1の説明が続きます
「図2-1 埋込みSQLアプリケーションの開発プロセス」の説明

図のように、プリコンパイルの結果、通常のコンパイルが可能な変更済ソース・ファイルが生成されます。従来の開発プロセスにプリコンパイル処理が追加されますが、このステップによりきわめて柔軟なアプリケーションを作成できます。

2.3 プログラミングのガイドライン

この項では、埋込みSQL構文、コーディング規則、C言語固有の機能および制限について説明します。簡単に参照できるように、項目をアルファベット順に記載してします。

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.2 定数

Lまたはlを接尾辞として付けると、long int型定数に指定されます。Uまたはuを接尾辞として付けると、符号なし整数定数に指定されます。0Xまたは0xを接頭辞として付けると、16進整数定数に指定されます。Fまたはfを接尾辞として付けると、float型浮動小数点定数に指定されます。これらの書式は、SQL文では使用できません

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;

この2つの文の間に指定できるのは、次の要素のみです。

  • ホスト変数および標識変数の宣言

  • 非ホスト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.6.3 CPP

CODE=CPPを指定してコンパイルすると、C++互換の関数プロトタイプが生成されます。このオプション設定は、C++コンパイラで使用してください。

2.3.7 ヒントの長さ

埋込みSQL文におけるSQLヒントの最大長は256文字に制限されます。この制限を超えるヒントはすべて切り捨てられます。

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.3.10 行の長さ

行の最大長は、ASCII文字のみを含む行の場合は1299、マルチバイト文字の場合は324です。

2.3.11 MAXLITERALのデフォルト値

MAXLITERALプリコンパイラ・オプションを使用すると、プリコンパイラで生成される文字列リテラルの最大長を指定できます。MAXLITERALのデフォルト値は1024です。必要に応じて、より小さい値を指定してください。たとえば、Cコンパイラで513文字以上の文字列リテラルを処理できない場合は、MAXLITERAL=512と指定します。使用しているCコンパイラのユーザーズ・ガイドを参照してください。

2.3.12 演算子

論理演算子と関係演算子「equal to」は、CとSQLでは次のリストのように異なります。これらのC演算子は、SQL文では使用できません

SQL演算子 C演算子

NOT

!

AND

&&

OR

||

=

==

次のC演算子も、SQL文では使用できません

タイプ C演算子

アドレス

&

ビット単位

&、|、^、~

コンパウンド代入

+=、-=、*=など

条件付き

?:

減分

--

増分

++

間接

*

モジュラス

%

シフト

>>、<<

2.3.13 文の終了記号

埋込みSQL文の終わりには、次のように常にセミコロン(;)を付けます。

EXEC SQL DELETE FROM emp WHERE deptno = :dept_number;

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.4.2 SELECT文の例

次の例では、記号site2が定義されている場合にのみ、SELECT文がプリコンパイルされます。

EXEC ORACLE IFDEF site2;
   EXEC SQL SELECT DNAME
       INTO :dept_name
       FROM DEPT
       WHERE DEPTNO= :dept_number;
EXEC ORACLE ENDIF;

C、C++または埋込みSQLコードをIFDEFとENDIFの間に記述し、シンボルを定義しないことで、そのコードをコメント行にすることができます。

2.5 分割プリコンパイル

複数のCまたはC++プログラム・モジュールを別々にプリコンパイルし、それらをリンクして1つの実行可能プログラムにできます。これにより、プログラムの機能コンポーネントの作成とデバッグを複数のプログラマが分担して行う場合に必要とされる、モジュラー・プログラミングが可能になります。個々のプログラム・モジュールを同じ言語で作成する必要はありません。

2.5.1 ガイドライン

次のガイドラインに従うと、いくつかの問題を回避できます。

2.5.1.1 カーソルの参照

カーソル名はSQL識別子であり、その有効範囲はプリコンパイル・ユニットです。このため、カーソルの動作が複数のプリコンパイル・ユニット(ファイル)にまたがることはありません。つまり、あるファイル内で宣言したカーソルを、別のファイルからオープンまたはフェッチすることはできません。したがって、分割プリコンパイルを実行するときは、指定のカーソルに対する定義と参照がすべて1つのファイルに記述されているか確認してください。

2.5.1.2 MAXOPENCURSORSの指定

OracleにCONNECTするプログラム・モジュールをプリコンパイルする場合は、すべてのプログラム・モジュールに十分に対応できるように、MAXOPENCURSORSの値を指定してください。CONNECTしない別のプログラム・モジュールにMAXOPENCURSORSを使用しても、その値は無視されます。実行時には、CONNECTに有効な値のみが使用されます。

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);
}