全て クラス 名前空間 関数 変数 型定義 列挙型 列挙値 ページ
API概要

読み取りオペレーション

サーバーカーソル

実際にTransactdを使用する前に、サーバーカーソルという概念を理解する必要があります。
データの読み取りにおいて、SQLでは複数の結果行ををローカルに取ってきて、各行をスクロールしながら処理していきます。 このスクロールの中で現在フォーカス(選択)している行(カレント行)をカーソルと呼んでいます。
通常SQLによる処理はクライアント側でのカーソルを使います。これをクライアントカーソルと呼びます。それに対して Transactdのオペレーションは基本的にサーバーカーソルを使用します。
サーバーカーソルは、1行づつスクロールする際にサーバー側で管理されるカレント行を移動していきます。たとえば、nstable::seekNext() というメソッドは、サーバー管理された現在のカレント行を次の行に移動してその行をローカルに転送するという処理をしています。

テーブルのjoinなどの集合演算

Transactdのサーバーサイドでは、SQLのjoinなど集合演算に相当する機能はありません。サーバーでのアクセスはすべて個別のテーブル単位です。 それらに相当する処理はすべてクライアントライブラリ(tdclcpp)内で行われます。

カーソルの確立

テーブルをオープンした直後はサーバーカーソルは存在しません。
カーソルを確立するには、インデックスを使用する方法と、レコードの物理順を使用する方法の2つがあります。

インデックスを使ったカーソルの確立

インデックスを使う場合はnstable::seek系オペレーションを使用します。
インデックスを指定するとき、各行はインデックス順に並んでいると考えてください。 nstable::seekFirst()nstable::seekLast()はそれぞれインデックス順に並べられた先頭行、最終行にカーソルを確立します。seekは事前に指定したインデックスフィールドの値を使い、seekタイプに従って、見つかった行にカーソルを確立します。
seekのタイプには以下の5つがあります。

カーソルが確立されると、nstable::seekPrev()nstable::seekNext()で1行づつ前後に移動できます。
各オペレーションの実行ステータスはnstable::stat()メソッドで読み出します。 サンプルコード レコードの読み取りと更新

物理順を使ったカーソルの確立

物理順は、レコードが挿入された順番です。(ただし、mysqlのinnodbエンジンでは厳密な物理順は存在せず、 primaryキー順と同じ並びになります。)
レコードの物理順によるオペレーションはstep系オペレーションを使用します。最初のカーソル確立は table::stepFirst()table::stepLast()の2つで、それぞれ物理順に並んだ先頭行、最終行にカーソルを確立します。
移動はtable::stepPrev()およびtable::stepNext()で行います。
各オペレーションの実行ステータスはnstable::stat()メソッドで読み出します。

フィールドへの読み取りアクセス

カーソルが確立できると、table::getFV系メソッドでその行のフィールドの値を読み取ることができます。
読み出す値の型に応じてtable::getFVbyt()table::getFVint()table::getFVlng()table::getFV64()table::getFVflt()table::getFVdbl()table::getFVstr()などがあります。
フィールドは、それぞれのメソッドの引数にフィールド番号(short)またはフィールド名(const _TCHAR*)で指定します。

フィールドへの値の設定

フィールドへの値のセットはtable::setFV(short index, int value)または table::setFV(const _TCHAR* fieldname, int value)のどちらかの関数で行います。 valueの型はintの他にも組み込み型とconst _TCHAR* がオーバーロードされています。
値のセットは、読み取りオペレーションの前にインデックスの値を指定する時と、 追加・更新オペレーションでフィールドの値をセットするときの両方で使用します。

フィールドの指定

フィールドの指定は番号と名前のどちらでもできますが、パフォーマンスを最大にしたいときは番号を使うようにしてください。 名前での指定は呼び出しごとに、名前から番号の検索が行われパフォーマンスが悪くなります。
番号で指定し、かつ、フィールドの追加などスキーマ変更に対して強いコードにするには、テーブルを開いた後で、 名前から番号を検索した結果を変数にキャッシュしておくようにします。 名前から番号の検索はtable::fieldNumByName()メソッドで行うことができます。

// フィールド番号をキャッシュする例(fdi_xxx変数にフィールド番号をキャッシュ)
static const short fdi_id;
static const short fdi_name;
fdi_id = table->fieldNumByName("id");
fdi_name = table->fieldNumByName("name");
// フィールド番号でアクセス
table->setFV(fdi_id, 1);
table->setFV(fdi_name, "yamada taro");

1回の通信で複数行の読み取り

サーバーカーソルは1行移動する度にネットワーク通信が発生するため、多くの行をまとめて読み取りたい場合には 効率の悪いことがあります。
Transactdでは、1回の通信でまとめて複数行を取得するfind系オペレーションが用意されています。
findオペレーションでは、フィルターを指定することで読み取りたい行を選択します。 フィルターの構文はSQL文のwhere句によく似ています。

query q;
q.where("id", "=" 1).or_("id", "=", 2);

(ただし、フィルターにはSQLに比べて制限があります。SQL文のwhere句と同等なものではありません。 詳しくはtable::setQuery()関数、queryクラスと基底クラスのqueryBaseを参照してください。)
また、queryでは1回に取得する最大行数(limit)と、フィルターにマッチしない行の最大数として 最大スキップ行数(reject)も合わせて指定します。
フィルターを適用する読み取り範囲の先頭は、前記seek系オペレーションによって確立された現在のカーソル位置です。
範囲の最後は、指定したインデックスの最後のレコードになります。ただし、最大スキップ行数に1を指定した場合は、 最初に見つかったフィルターにマッチしない行の前の行が最後になります。
パフォーマンスを向上するには、テーブルの最後の行までスキャンされないように、最大スキップ行数に1を指定し、 検索範囲の最後を絞り込みます。
取得した行の範囲を移動するにはfindPrev()findNext()を使用します。それぞれ、limitの最後に達すると、 自動で次のlimitの行を取得して返します。プログラマーから見るとlimitは透過的で、その値に何を指定しても 得られる結果は同じです。(ただし、パフォーマンスには違いが出てきます。)

サンプルコード レコードのセットの読み取り

注意
find系オペレーションの実行後のサーバーカーソルの位置は不定です。カーソル位置が重要とな るオペレーション(updatedelなど)は実行しないようにしてください。実行した場合の結果も不定です。

行の挿入

レコードの挿入は、フィールドに値をセットしtable::insert(bool ncc)メソッドを実行します。
事前にカーソルが確立されていた場合、挿入後のカーソル位置を元のままにする(No Currency Change)か、 挿入した行の位置にするかを、nccパラメータで指定できます。

サンプルコード レコードの挿入

行の更新

更新の対象行は現在のカーソル行になるので、予め更新したい行にカーソルを移動しておきます。
あとはレコードの挿入と同じように、更新したいフィールドに新しい値をセットし table::update(eUpdateType type = changeCurrentCc)メソッドを実行します。
もしインデックスフィールドを変更した場合は、更新後のカーソル位置を更新前と同じにするか、更新後の位置にするか typeパラメータで指定できます。 (更新前と同じ位置の行は厳密には存在しなくなっていますが、table::seekPrev()table::seekNext()によって 移動する先は正しいものとなります。)

changeInKey

typeパラメータにeUpdateType::changeInKeyを指定するとnstable::keyNumで指定したキーを使って レコードを移動し、更新します。この場合、事前に更新したい行へ移動しておく必要がありません。
ただし、これを利用するためには以下の条件を満たす必要があります。

サンプルコード レコードの読み取りと更新

行の削除

削除の対象行は現在のカーソル行になるので、予め削除したい行にカーソルを移動しておきます。
あとはdel()メソッドを実行します。
table::del(bool inkey = false)を実行するとカーソル行は存在しなくなりますが、table::seekPrev()table::seekNext()table::stepPrev()table::stepNext()によって削除した行の前後に移動できます。

changeInKey

inkeyパラメータにtrueを指定するとnstable::keyNumで指定したキーを使ってレコードを移動し、削除します。 この場合、事前に削除したい行へ移動しておく必要がありません。
ただし、これを利用するためには以下の条件を満たす必要があります。

サンプルコード レコードの削除

トランザクション

トランザクションは、nsdatabase::beginTrn()nsdatabase::endTrn()nsdatabase::abortTrn()関数で、 開始、コミット、中止の処理ができます。
beginTrn()の呼び出し以降に実行されたinsert()update()del()によるすべての変更を成功させるか、 または行わなかったことにするか、endTrn()もしくabortTrn()の呼び出しで決定できます。
また、トランザクション内でカーソルの確立、移動を行うと、そのレコードはロックされます。

ロック

ロックには、シングルレコードロックとマルチレコードロックがあります。
シングルレコードロックはそのテーブルの最後移動したレコードのみがロックされます。 マルチレコードロックはトランザクション内で読み出したすべてのレコードがロックされます。
ロックされたレコードを他のクライアントが読み取ろうとすると、ロックが解放されるかタイムアウトするまで 待たされるようになります。
他のクライアントへの影響を少なくするには、シングルレコードロックの使用を検討してください。 どちらのロックを使用するかはnsdatabase::beginTrn()への引数で指定できます。
トランサクション内で更新されたレコードは、シングルレコードロック、マルチレコードロックの指定にかかわらず、 コミットされるまでロックされます。

サンプルコード トランザクション処理

トランザクションは、同じデータベース内であれば複数のテーブルにまたがって適用できます。
複数の更新処理をトランザクション内で行うと一般的にパフォーマンスは良くなります。しかし、長い時間 のかかるトランザクションは他のクライアントの処理をブロックする恐れがあるので、 なるべく短い時間で完了するようにします。
(mysqlのinnodbエンジンは完全な行ロックを提供しているので、PSQLのエンジンより並列実行性が高く、 他のクライアントのオペレーションをブロックすることは少なくなっています。
PSQLのエンジンも行ロックを提供しますが、インデックスページはページロックのため、更新や追加では よりロック範囲が多くなります。 特に複数の行の追加は同じインデックスページに書き込むことが多く容易にブロックが発生します。)

クエリーと結果セット

Version 2.0より、setFilterによりフィルタリングされた複数レコードの読み取り結果を、 配列をエミュレートした結果セットrecordsetオブジェクトにまとめて受け取れるようになりました。
また、その結果セットに別のテーブルの値をJoinやUnionすることもできます。 さらにrecordsetクラスは、結果セットに対するOrderByやGroupByといった処理もサポートします。
recordsetに結果を受け取る場合は、通常のtebleではなくactiveTableクラスを使用してアクセスします。 以下に簡単な例を示します。

activeTable User(db, "user");
query q;
q.select("id", "name", "group").where("id", "<=", 15000);
recordset rs;
User.index(0).keyValue(1).read(rs, q);
field fd = rs[0]["id"];

1行目はactiveTableオブジェクトにdatabaseオブジェクトを渡して"user"テーブルとして初期化します。
2行目はフィルタリングするためのクエリーオブジェクトを生成します。
3行目は、selectで取得するフィールドとしてid, name, groupの3つを指定し、whereでidが15000以下のレコードを取得するようにクエリーオブジェクトを設定しています。
メソッドチェーンをサポートするために、queryactiveTableクラスのほとんどのメソッドは、*thisを返します。
4行目は、結果を受け取るrecordsetクラスのインスタンスを生成しています。
5行目で、activeTableで使用するインデックスの番号と、検索を開始するキー値(キーフィールドの値)を指定し、 readメソッドにrecordsetとクエリーを渡して読み取ります。
6行目、recordsetから各行の読み出しは、配列と同じように[]演算子で取り出しできます。
各行はrowオブジェクトで、["id"]のように[]演算子でidフィールドを取り出しできます。また、フィールド名でなく[1] のようにフィールド番号で取得することも可能です。

詳細

文字コード

まず、文字コードには以下のものがあります。

文字列フィールドの値の文字コードはスキーマにて指定します。Transactdクライントは自動でクライアントプログラムで使用する文字コードに変換します。
クライアントプログラムで使用する文字コードは、テーブルやフィールドの名前などを引数に取る関数で使用する文字コードです。 クライアント側は、C++の場合WindowsとLinux・Mac、またUnicodeコンパイルとマルチバイトコンパイルなどの組み合わせによって変わってきます。 WindowsではUnicode、Linux・Macではutf-8が標準となっています。
サーバー側で設定された文字コードとはmy.cnfのcharset-serverで指定した文字ーコードです。transactd内部から発行される一部のSQL文はこの文字コードに変換する必要があります。
これらの各種文字コードの種類が異なる場合は変換が必要になります。 実行環境や、コンパイルオプションの組み合わせによってどのようにしたらよいかを示します。

C++クライアント
タイプ

項目

ターゲットサーバー 

Transactd PSQL
tdclcpp_x_32u.dll
tdclcpp_x_64u.dll
(Windows UNICODE)
サーバー文字コードの設定
(my.cnf charset-server)
UTF8または漢字の使用可能なcharsetを指定します。
charset-server = utf8
サーバーに指定するコードページはOSのコードページにしてください。サーバーOSのコードページとクライアントOSのコードページは同じである必要があります。
tabledef::schemaCodePage スキーマは任意のマルチバイト文字コードで作成できます。schemaCodePageはスキーマに使用するコードページを指定してください。 テーブル名やファイル名をセットする前に指定します。テーブル名やファイル名のセット関数を呼び出すと、内部でschemaCodePageに変換して保存します。
tabledef::schemaCodePage = CP_932;

関数のURIパラメータ 関数のURIパラメータはUNICODEで渡します。URIの送信はUTF8に変換されて行われます。
database::create(L"tdap://localhost/販売?dbfile=販売.bdf");
関数のURIパラメータはUNICODEで渡します。URIの送信はOSのコードページに変換されて行われます。
関数のフィールド名パラメータ 関数のフィールド名パラメータはUNICODEで渡します。フィールド名はUNICODE からスキーマのコードページに変換されて検索されます。
table::setFV(L"名前", L"akio");

tdclcpp_x_32m.dll
tdclcpp_x_64m.dll
libtdclcpp_64m.so
(WindowsマルチバイトLINUX他)
サーバー文字コードの設定
(my.cnf charset-server)
UTF8または漢字の使用可能なcharsetを指定します。
charset-server = utf8
スキーマはサーバーに指定したコードページで作成してください。
tabledef::schemaCodePage スキーマはUTF8で作成してください。
schemaCodePageはUTF8を指定します。
tabledef::schemaCodePage = CP_UTF8;
schemaCodePageはサーバーに指定したコードページを指定してください。
関数のURIパラメータ 関数のURIパラメータはUTF8で渡します。URIの送信は変換せずに行われます。
database::create(u8"tdap://localhost/販売?dbfile=販売.bdf");
関数のURIパラメータはサーバーに指定したコードページで渡します。URIの送信は変換せずに行われます。
関数のフィールド名パラメータ 関数のフィールド名パラメータはUTF8で渡してください。フィールド名はUTF8同士で正しく検索できます。
table::setFV(u8"名前", "akio");
関数のフィールド名パラメータはサーバーに指定したコードページでで渡してください。フィールド名はサーバーに指定したコードページ同士で正しく検索できます。

文字コードを正しく扱うにはまず、実行環境の文字コード(execCodepage)とベータベースに保存された文字列の文字コード(codepage)の2つがあることを意識します。 実行環境の文字コードはオペレーティングシステムと言語によりほぼ決まってきます。 windowsの多くの言語はutf16を主体とします。linuxの場合はutf8を主に使用します。 transactdの文字コード変換はすべてクライアント側で行われます。サーバー側での変換は一切ありません。

execCodepageは、char型の文字列のコードページが何であるかを示します。LINUX版はデフォルトでUTF-8です。 Windows版は、CP_ACPの値がデフォルトです。Windowsで、SetFV関数などで、フィールドの値をUTF-8で渡す場合は、事前に nsdatabase::setEexecCodepage() でCP_UTF8を指定しておく必要があります。 ワイド文字 wchar_t型のSetFV関数では、execCodepageが何であるかは関係ありません。

linuxでは libtdclcpp_64m.soはすべてマルチバイト文字セットとしてコンパイルされます。_TCHARマクロはchar型としてコンパイルされます。 また、execCodepageのデフォルトはGetACP()マクロが 常に utf8(コードページ 65001)を返すようになっています。 このコードページでなくたとえばcp932で処理したい場合は、 nsdatabase::setExecCodePage()を使ってexecCodepageを変更します。

次に、データベースの文字コード(codepage)は fielddef::charsetIndex()が参照されます。このcodepageとexecCodepageが異なる場合 文字コードの変換が行われます。windowsでの変換はosのWideCharToMultiByteとMultiByteToWideCharを使って行われます。 linuxはiconvを使って変換されます。linuxでは変換を高速に行うために、文字コードの組み合わせごとに変換ライブラリをオープンしてキャッシュしています。 デフォルトのキャッシュは mbcs utf16 utf8の3つの文字コードのすべての組み合わせの6つがされます。mbcsの文字コードはデフォルトで"SHIFT-JIS" が mbcswchrLinux.hのMBC_CHARSETNAMEマクロで指定されています。デフォルトのmbcsを変更する場合は、MBC_CHARSETNAMEを変更してコンパイルし直す必要があります。 また、複数のmbcsを同時に使用する場合は、mbcswchrLinux.h及びmbcswchrLinux.cppのコードにそれを追加するための変更を加える必要があります。

データタイプ

fielddefクラスのtype属性で指定できるフィールドタイプを説明します。 ここで説明するフィールドタイプのほとんどはTransactdからは問題なく使用できますが SQLからのアクセスにおいては、期待通りに読み取れないものがあります。 Transactdでは、フィールドのバイナリーフォーマッティングをクライント側で行っています。 TransactdでのアクセスだけでなくSQLからも正しく読み取れるようにするには、両方で 正しく読み取れる型を選択します。 MySQLのデータベースを使用する場合は、string, zstring, autoinc、integer、uinteger 、floatおよびmyが先頭に付くデータタイプを使って構築してください。MySQLのmoneyとdecimal型は現在のところtransactdは未対応です。
PSQLのデータベースを使用する場合は、myが先頭に付かない型を使用してください。 PSQLで、myの付いた型はキーフィールドには使用できません。使用するとテーブルの作成でエラーになります。

C++クライアントでは、フィールドにC++の組み込み型で値をセットすると 自動でそれぞれの型にフォーマットされます。

データタイプ対応表 も参照してください。

CHAR型 VARCHAR型のフィールド長さ

Transactdスキーマではフィールドの長さをすべてバイトで指定します。
しかしながら、MySQLのCHAR、VARCHAR型では文字数での指定のため変換が必要になります。 ここではその変換の計算方法について説明します。 最初に文字コードごとに違う1文字当たりのバイト数を lenByCharnum()関数を使って取得します。 CHARの場合長さは1文字当たりのバイト数 × 最大文字数がフィールドの長さになります。 VARCHARの場合は、文字の長さを表すバイトが1または2バイトが追加で必要になります。 1文字当たりのバイト数 × 最大文字数が255バイト以下の場合は1、それ以上は2バイトを 付加します。 また、 fielddef::setLenByCharnum()関数を使うと、文字数で長さを指定することができます。 その際、事前に fielddef::setCharsetIndex()関数で文字コードを、 fielddef::typeにフィールドタイプを 指定しておく必要があります。

オブジェクトのスレッド安全

Transactdクライントのオブジェクトはスレッド間での共有は安全ではありません。スレッドごとに 専用のオブジェクトを使用する場合は安全です。
Webサーバーアプリケーションで、複数のスレッドで処理を行う場合はスレッドごとに異なるdatabaseオブジェクトを 使用してください。

コネクションプール

Webサーバーアプリケーションではサーバーとの接続のオーバーヘッドを減らすためにコネクションプール の使用を検討してください。コネクションプールにアクセスし利用するには pooledDbManager クラスを使用します。コネクションプールを使う例は以下のサンプルを参照してください。

サンプルコード コネクションプールを使う

Transactd SDK 2018年07月31日(火) 19時40分24秒 doxygen