Btrieve 2 API と C/C++ の使用

このチュートリアルでは、Btrieve 2 SDK と C++ を使用して Actian Zen データベース エンジンに接続する方法を学習します。

右側の「このページの内容」では当ページで扱うトピックを示しています。

Zen データベースとの接続

Actian Zen の Btrieve 2 SDK は、データ ファイルおよびデータを直接処理するのに適した、成熟した API を提供します。テーブルと列の概念を使用する SQL ベースのデータベース アクセスとは異なり、Btrieve 2 は、ファイル システム内の個々のファイルに格納されたデータ レコードへの直接アクセスを提供します。

プロジェクトで Btrieve 2 を使用するには、以下を行う必要があります。

  • Actian Zen をインストールします。
  • Btrieve 2 SDK を開発環境にダウンロードして展開します。
  • C++ アプリケーションを作成します。

Btrieve 2 API は標準 C++ クラス ライブラリとして実装されるため、アプリケーション環境と直接統合されます。

Btrieve 2 はあらゆるプロジェクト構成で機能し、含まれているライブラリは 32 ビットと 64 ビットの両方のプロジェクトをサポートします。プロジェクト プロパティに必要な調整は、コンパイラおよびリンカーから正しいヘッダーとライブラリが見えるように、プロジェクトでそれらをリンクすることだけです。

2 つのライブラリがあります。

  • btrieveCpp.lib は C++ クラス ライブラリです。これは、btrieveC.lib に定義されている実装のラッパーで、リンケージ エディターの設定で追加の依存関係としてリストに含まれている必要があります。
  • btrieveC.lib は C ライブラリです。実装が含まれており、データベース エンジンと直接通信します。これは btrieveCpp.lib の依存関係であるため、追加の依存関係リストから除かれます。

32 ビット ライブラリと 64 ビット ライブラリ自体の名前は同じで、btrieveCpp.lib および btrieveC.lib ですが、それぞれ SDK パッケージの win32 フォルダーおよび win64 フォルダー下に提供されます。

include フォルダー内の同じヘッダー ファイルが、すべての構成で使用されます。両方のヘッダーが必要ですが、1 番目の btrieveCpp.h のみが直接インクルードされ、2 番目は間接的にインクルードされます。

  • btrieveCpp.h は btrieveCpp.lib に対応しており、プログラムに直接インクルードされます。
  • btrieveC.h は btrieveC.lib に対応しており、btrieveCpp.h を介して間接的にインクルードされます。

Btrieve 2 API を使用して Zen エンジンとやり取りするには、まず、アプリケーション コードに btrieveCpp.h ヘッダーをインポートする必要があります。

#include "btrieveCpp.h"

これは、ガイド内のその他すべての例で前提となるため、すべての例でこの行を繰り返すことはしません。

エンジンに接続するには、btrieveClient オブジェクトのインスタンスを作成する必要があります。

BtrieveClient btrieveClient (0x4232, 0) ;

最初のパラメーターは serviceAgentIdentifier(0x4232 = "B2")で、エンジンに対するアプリケーションの各インスタンスを識別します。"AA"(0x4141)より大きい任意の 2 バイト値を指定できます。

2 番目のパラメーターは、2 バイト整数の clientIdentifier です。マルチスレッド アプリケーションを作成している場合は、各スレッドが、エンジンに対する一意のクライアント識別子を提供する必要があります。ここに示す簡単な例では、0 を使用します。

データ ファイルの作成とデータの挿入

データの格納を開始するためには、データ ファイルを作成し、それにレコードを追加する必要があります。そのため、このセクションでは以下のことを調べます。

  • ファイルの作成
  • データ レコードの挿入

ファイルの作成

すべての Zen データはファイルに格納されます。各ファイルは、データのバイトを含むレコードにデータを格納します。通常、特定のファイルのレコードは、関連するデータ値の集合体を表します。

この例では、血圧測定値を格納するファイルを作成します。各レコードは以下の情報で構成されています。

  • 8 バイト タイムスタンプ - 血圧測定値の採取日時
  • 2 バイト整数 - 収縮期の値
  • 2 バイト整数 - 拡張期の値
  • 1 バイト文字 - 評価コード

上記のデータを考えると、13 バイトのレコードを保持するファイルを作成する必要があります。以下のスニペットは、血圧レコードを収容できる BPrecord_t 構造体を定義しています。#pragma pack ステートメントは、レコードが実際に 13 バイトであり、コンパイラによって余分なアライメント バイトが追加されていないようにするために必要です。

#pragma pack(1)
typedef struct {
  uint64_t timeTaken;
  uint16_t systolic;
  uint16_t diastolic;
  char EvalCode;
} BPrecord_t;
#pragma pack()

このようなレコード用のファイルを作成するには、btrieveFileAttributes オブジェクトを割り当て、SetFixedRecordLength プロパティを使用してレコード サイズを指定する必要があります。

Btrieve::StatusCode status;
BtrieveFileAttributes btrieveFileAttributes;
status = btrieveFileAttributes.SetFixedRecordLength(sizeof(BPrecord_t));

他のファイル属性を追加することができますが、必要なのはレコード サイズだけです。たとえば、ファイルをディスクに書き込む前に圧縮するように指定できます。

次に、btrieveClient セッション オブジェクトで FileCreate() メソッドを使用してデータ ファイルを作成するよう、Zen エンジンに指示します。また、ファイルが既存である場合に、上書きするかどうかを示す作成モードを指定することもできます。

static char* btrieveFileName = (char*)"Pressures.btr";
status = btrieveClient->FileCreate(&btrieveFileAttributes, btrieveFileName,
                                                  Btrieve::CREATE_MODE_NO_OVERWRITE);
if ((status != Btrieve::STATUS_CODE_NO_ERROR) &
    (status != Btrieve::STATUS_CODE_FILE_ALREADY_EXISTS))
{
printf("Error: BtrieveClient::FileCreate():%d:%s.\n", status,
                   Btrieve::StatusCodeToString(status));
}

上記は、Btrieve クラスの組み込みの StatusCode 列挙を使用してエラー チェックを行う例を示しています。簡潔にするため、後続のコード サンプルではエラー チェックを示しません。

データ レコードの挿入

ファイルへのデータの挿入は、BtrieveFile オブジェクトの RecordCreate メソッドによって行えます。ただし、レコードを挿入する前に、ファイルを開いておく必要があります。

BtrieveFile btrieveFile;
status = btrieveClient->FileOpen(btrieveFile, btrieveFileName, NULL,
                                              Btrieve::OPEN_MODE_NORMAL);

上のコマンドにより、btrieveFile オブジェクトを割り当て、btrieveClient セッションを使用して、以前に作成したファイルを開きます。3 番目のパラメーターを使用して Btrieve オーナー ネームを渡すことができます。オーナー ネームは、データ ファイルをセキュリティ保護(および、任意で暗号化)するためのパスワードです。作成したファイルはオーナー ネームを持っていないので、NULL を渡すだけでかまいません。

レコードを挿入するために、前に作成した BPrecord_t 構造体用のレコード バッファーを割り当てます。その後、次の例で示すように、レコード構造体のメンバーにデータ値を入れて、そのレコードを挿入します。

Btrieve::StatusCode status = Btrieve::STATUS_CODE_NO_ERROR;
BPrecord_t record;
  // Get current system time and convert to microseconds 
time_t now = time(0) * 1000000;
  //Convert time to Btrieve 2 Timestamp format 
record.timeTaken = Btrieve::UnixEpochMicrosecondsToTimestamp(now);
  //sysdata and diasdata are provided at runtime 
record.systolic = sysdata; 
record.diastolic = diasdata;
  //Determine the EvalCode from the systolic & diastolic 
record.EvalCode = 'N';       // Default is Normal
if ((sysdata >= 120 and sysdata < 130) and (diasdata < 80)) 
     record.EvalCode = 'E';    //Elevated blood pressure
if ((sysdata >= 130 and sysdata < 140) or (diasdata >= 80 and diasdata < 90)) 
     record.EvalCode = 'H';      //High blood pressure
if ((sysdata >= 140 and sysdata <= 180) or
    (diasdata >= 90 and diasdata <= 120)) 
     record.EvalCode = 'V'; //Very high blood pressure
if ((sysdata > 180) or (diasdata > 120))
     record.EvalCode = 'C';  //Hypertensive Crisis 
// Insert the record 
status = btrieveFile->RecordCreate((char*)& record, sizeof(record));

もちろん、ステータス(status)を調べて、挿入が成功したことを確認する必要があります。

インデックス作成とデータの取得

これまでに、Btrieve 2 アプリケーションを使用する Actian Zen エンジンとのセッションをセットアップし、データ ファイルを作成して、そのファイルにレコードを挿入する方法を学習してきました。Zen の真の実力は、インデックス作成と高速なデータ取得に見られます。次に、この機能を調べます。

レコードのインデックス作成

Zen ファイルの最も強力な機能のうちの 1 つは、インデックス作成です。既存のデータ ファイルにインデックスを追加できるため、特定の値によって、または特定の順序で簡単かつ迅速にレコードを取得することができます。また、新しく作成したファイルに、レコードが挿入される前にインデックスを追加することもできます。

インデックスの作成には 3 つの手順があります。

  1. インデックス セグメントをセットアップする。
  2. インデックス属性を定義する。
  3. データ ファイルにインデックスを追加する。

引き続きサンプル ファイルを使用して、レコードのタイムスタンプ部分にインデックスを追加します。ファイルを開いておかないとインデックスを追加できないので注意してください。

BtrieveKeySegment btrieveKeySegment;
BtrieveIndexAttributes btrieveIndexAttributes;

// Create a time stamp index segment on the first 8 bytes of the record 
status = btrieveKeySegment.SetField(0, 8, Btrieve::DATA_TYPE_TIMESTAMP);
// Add the segment to the Index object 
status = btrieveIndexAttributes.AddKeySegment(&btrieveKeySegment);
// Specify the nonmodifiable index attribute 
status = btrieveIndexAttributes.SetModifiable(false);
// Create the index 
status = btrieveFile->IndexCreate(&btrieveIndexAttributes);

インデックス セグメントは、レコードのオフセット 0 から始まるフィールドとして定義され、長さは 8 バイトです。その 8 バイト内のデータは、Btrieve TIMESTAMP として解釈されます。

AddKeySegment() メソッドは、btrieveKeySegment インスタンスを btrieveIndexAttributes オブジェクトに追加して、単一セグメント キーを定義します。

SetModifiable() メソッドは、インデックスを変更可能(true)または変更不可能(false)として指定します。他の属性を使用して、インデックスを一意にしたり、特定のインデックス番号を指定したりすることができます。

IndexCreate() メソッドは、以前に開いた、BtrieveFile オブジェクトに関連付けられているデータ ファイルにインデックス 0 を追加します。インデックスは、現在ファイルにあるすべての値を伴って作成され、後続のすべての挿入/更新/削除操作で自動的に更新されます。

レコードの読み取り

インデックスを作成した後、レコードを取得するのは簡単です。インデックス定義に従って先頭または末尾のレコードを取得したり、指定された値と比較して特定の値を取得したりするためのメソッドがあります。この例では、タイムスタンプ値が最も高いレコード(挿入したばかりのレコードに相当する)を取得しています。

レコード取得メソッドは、以前に見てきた他の呼び出しのようなステータス コードを返しません。代わりに、取得されたレコードのサイズが関数呼び出しによって返されます。サイズが期待どおりに返されない場合は、GetLastStatusCode() メソッドを呼び出して、何が起こったのかを調べることができます。

Btrieve::StatusCode status = Btrieve::STATUS_CODE_NO_ERROR; 
BPrecord_t record;
// Retrieve last inserted record
if (btrieveFile->RecordRetrieveLast(Btrieve::INDEX_1, 
    (char*)& record, sizeof(record),
     Btrieve::LOCK_MODE_NONE) != sizeof(record))
{
     status = btrieveFile->GetLastStatusCode();
     printf("Error: BtrieveFile::RecordRetrieve():%d:%s.\n", status,
              Btrieve::StatusCodeToString(status));
}

レコード取得メソッドの最後の引数は、取得中にレコードをロックするオプションを提供します。

レコードを取得したら、それを更新するか削除するかを決定できます。まずレコードを取得しなければ、レコードを更新/削除することはできません。これらの操作には、btrieveFile->RecordUpdate() メソッドおよび btrieveFile->RecordDelete() メソッドが使用されます。

クリーンアップ

プログラマーの責任として、プログラムのクリーンアップを必ず行ってください。ファイルの作業が完了したら、btrieveClient オブジェクトで FileClose() メソッドを呼び出して、ファイルを閉じます。

status = btrieveClient->FileClose(btrieveFile));

あまり必要としないでしょうが、Btrieve 2 もデータ ファイルを削除するためのメソッドを提供しています。

status = btrieveClient->FileDelete(btrieveFileName);

アプリケーションを閉じる前に、Reset() メソッドを呼び出して、エンジンとのセッションを解放する必要があります。

status = btrieveClient->Reset();

このチュートリアルの例も含め、Btrieve 2 API の追加のドキュメントおよび例はオンラインで入手可能です。