Tech Note 02: Palm OS データベースに関して

Jan 05, 2005

© NSB Corporation. All rights reserved.

[英語版]

NSBasic/PalmでのPalmデータベース使用の概要

NS Basic/Palmを使って作成するものも含め、Palmデバイス上の全てのデータベースは同じフォーマットを持っています。NS Basic/Palmを使って作成するデータベースは、デフォルトとしてバックアップビットをセットしてありますので、データベースは"x:\palm\{username}\backup"にコピーされます。("x"はあなたのHotsyncソフトをインストールしたディレクトリーです)一旦そこに入るとデータの処理を行うことが出来ます。Palmプログラムは.pdbファイルの特殊フォーマット.prcファイルとして保存されます。

NS Basic/PalmはPalmデバイス上でしか動作しません。Palmデバイスは.pdbのデータベースしか認識しません。NS Basic/Palmは.pdbファイルの全てにアクセスすることができます。NS Basic/Palmで作成されたデータベースは、自動的にHotsync用にデスクトップにコピーされます。NS Basicユーザーに便利な、Access, CSV, Excel, および他のフォーマット間の変換を行う様々なユーティリティ・ソフトが、本稿の最後に記載されていますので、必要に応じてご利用下さい。また、常にサポート掲示板等を確認し、このトピックに関する情報を得て下さい。

Palm OSはデータベースのレコードに何が入ったかは特に気にしません。Palm OSはデータベース・レコードを単なる一連のバイトとしか考えていません。あなたのアプリケーションはそれらのバイトを、自分が必要としている形式で、解釈/翻訳していかなくてはなりません。

このテクニカルノートのサンプルはここからダウンロードできます。

 

キーを使わないデータベースの読み書き

これはデータベースへのアクセスとしては最も単純な方法かもしれませんが、時に多くの混乱を招きます。まず、NSBasic/Palmにあるデータベースを一種の「グリッッド」だと思って下さい。グリッドの各「セル」はデータの1バイトを確保します。NSBasic/PalmハンドブックにあるDIMステートメントの説明にNSBasic/Palmのデータタイプとそれらの解説を表にしてあります。文字列(String)は例外で、1文字毎に1バイトが、さらにターミネーターとして1バイトが必要です。"Cat"という文字列をデータベースの1つのレコードの始まりから書くとすると、以下のような具合になります。('/0'は文字列の最後を示す記号)

C a t /0                

キーを使わないデータベースへの書き込みは単純ですが、どこにデータを納めたかを分かっている必要があります。日付け(Date)や数値は簡単で、8バイトの値(Date、Time、など)または4バイトの値(Integer、Single、など)に変換します。一様でない文字列を入れる場合は、必要な長さに文字列を置き換える必要があります。これを行わないとDmWriteCheck exceptionsを発することになります。文字列"Cat"を含むフィールドを15文字の長さにする場合は、Palmのメモリは以下のような具合になります。

C a t                         /0        

 

キーを使わないデータベースに書き込む時は2つのファンクション(dbPosition()とdbPut())を使う必要があります。dbPosition()はレコードポインタを特定のレコード番号のオフセット(上記で話したセル)にセットします。dbPut()を使用してデータを書き込んだ時、レコードポインタは最後に書かれたデータの終わりにセットされます。以下の例では、fldNameというフィールドオブジェクトに名前が入っています。このコードを実行することにより、fldNameの中身をデータベースの最初のレコードの始まりへと書き込みます。

'assume name is DIMed as String and db as Database
name = rightpad(fldName.text,15) 'set the string to be 15 characters long.

res = dbposition(db, 1, 0)
res = dbput (db,name)
if res not = 0 then
    msgbox "Write error: "+str (res)
end if

同じレコードにさらにデータを書き込むには、dbPutステートメントをくり返し使うだけです。

'assume that dDate is DIMed as a Date type
res = dbput (db,dDate)
if res not = 0 then
    msgbox "Write error: "+str (res)
end if

'assume fmyFloat is DIMed as a Float
res = dbput (db,fmyFloat)
if res not = 0 then
    msgbox "Write error: "+str (res)
end if

これらのデータを読み出す時は、dbPosition()とdbGet()を使用します。dbGetを呼ぶとレコード内でポインタは動きますので、dbPositionをくり返し呼ぶ必要はありません。(特定のフィールドだけを欲しい時に使用)

res = dbposition(db, 1, 0)
res = dbget (db,name)
if res not = 0 then
    msgbox "Read error: "+str (res)
end if
fldName.text = rtrim(name) ' removes the spaces padded on by the rightpad() function.

dDate=-1
res = dbget (db,dDate)
if res not = 0 then
    msgbox "Read error: "+str (res)
end if 
fldDate.text = datemmddyy(dDate) 'convert the date into a displayable format res = dbget (db,fmyFloat) if res not = 0 then msgbox "Read error: "+str (res) end if fldCost.text = str(fmyFloat)

多くのデスクトップユーティリティで作成される.pdbファイルはキーを持っていません。これらは「データベース内のポジションによるオペレーション」のファンクションを使用してアクセスして下さい。NSBasic/Palmハンドブックの10.Dを参照して下さい。

キーを使うデータベースの読み書き

キーを使うデータベースへの書き込みは、各レコードに与える特有のID(キー)が必要になります。NSBasicデータベースのヘッダーは、オフセットと共にキー情報によってデータの保管場所を管理します。この方法の2つの大きなメリットは、アクセスと保管が容易なことです。レコードを検索するには、キーによって検索するだけです。(キーを使わないデータベースは一つのレコード毎に確認が必要です)キーを使うデータベースはさらに、ユーザー定義のタイプ(UDT:TYPEステートメントによって作成)を使ってデータを保管することが出来ます。例えば50アイテムを書き込む場合、dbPutをアイテム毎に使用する代わりに、この方法を使うと、3つの引数と共に一回のdbInsert()で済みます。

dbInsert (database,keyvalue,UDT)

上記で説明したキーを使わないデータベース同様、次のコードは一つの文字列、一つの日付け、一つの少数をPalm PDBファイルに書き込みます。以下はUDTと仮定。

type myData
    name as string
    dDate as Date
    fmyFloat as float
End type

次に、以下の変数をProject Startupコード内で定義されると仮定。

Global db as Database
Global msgdata as string
Global recordData as myData

dbInsertコマンドを使ってデータベースへの書き込みは以下のようになります。(この時点でデータベースは作成されていて、開いていると想定しています)

recordData.name="John Doe"
recordData.dDate = mmddyytodate("09/29/99")
recordData.fmyFloat = val("123.45")

Dim res as Integer
Dim keyval as Integer

keyval=1
res=dbinsert (db,keyval,recordData)

これはキーの値に1を持つレコード(上記データ)をデータベースに書き込みます。
データの読み出しはほぼ同じで、次のようになります

keyval=1 ' must be set to the key of the record you want to retrieve

res=dbread(db,keyval,recordData)
if (res = 0) then
    msgbox recorddata.name
    msgbox datemmddyy(recorddata.dDate)
    msgbox str(recorddata.fmyfloat)
else
    alert ("Oops!","An error occured : "+str(res),3,"OK")
end if

'res'に2が戻されると、キーで指定したレコードは見つからず、代わりにその次に近いレコードが戻されたことを意味します。

キーを使うデータベースについてはNSBasic/Palmハンドブックの10.D.b「レコード・キーによるオペレーション」もご覧下さい。さらにキーに続き番号を使う例を10.D.c「連続番号を持つキーを使う」で説明しています。

"データベース"変数について

ここまでの時点でお気付きかもしれませんが、全てのデータベースへアクセスするステートメントは"データベース"変数を使って(開いている、閉じている、消されている、または変更が加わった)データベースを参照します。この変数の使用を理解することは、後になってデバッグが大変になる、よくあるミスを避けられるでしょう。

Palm OSのアプリケーションによってデータベースが作成または開かれた場合、データベースを参照する番号("ハンドル")がオペレーティングシステムによって割り当てられます。このハンドルがNSBasicデータベース変数に代入されます。

Dim db1 as database ' Palm OS データベースハンドルを保管する変数
Dim res as integer ' 結果変数
res=dbCreate(db1,"MyDB",0,"Test")
res=dbOpen(db1,"MyDB",0)

後に続くデータベースオペレーションは、データベース名ではなく、このハンドルによってデータベースを参照します。

'例...
res=dbFind(db1,"Doe, John")
res=dbInsert(db1,dbKey,dbData)
あなたのアプリケーションはOSからこのハンドルを受け取りますが、決してこれを変更しないでください。変更してしまうと、OSがデータベースへのアクセスを失います。さらに、同じデータベース変数を使って、別のデータベースを決して開こうとしないでください:
res=dbOpen(db1,"MyDB",0)
res=dbOpen(db1,"NextDB,0) ' db1は"MyDB"を参照しなくなった
res=dbOpen(db1,"NextDB",0) ' db1は"NextDB"を参照しますが、読み込み専用で開いています

複数のデータベースを参照する適切な方法は、異なったハンドルを使うために、別々のデータベース変数を使います:

Dim dbInventory as database
Dim dbPrices as database
res=dbOpen(dbInventory,"MyDB",0)
res=dbOpen(dbPrices,"NextDB,0)
'両方のデータベースが開いた状態、各ハンドルは別々のデータベース変数に保存
'...データベース処理と他のコード...
res=dbClose(dbPrices)
res=dbClose(dbInventory)
'両方のデータベースは閉じた状態、必要であれば再度開いても問題はない

プログラマーによっては全てのデータベースをアプリケーションの始めに開き、終了のときに全て閉じます。また、プログラマーによっては必要なときにデータベースを開き、データへの処理が終わると同時にデータベースを閉じます。どちらの方法を使うかはあなた次第ですが、全てのデータベース処理の戻り値を確認し、エラーのチェックを行ってください。

PDBとPRCを一緒に使う

データベース (PDB) はアプリケーション (PRC) の中には保存されません。これらは分かれています。データベースには3つの属性(Creator ID、タイプ、名前)があります。Creator IDおよびタイプは共に4バイト長の値で、'appl', 'REAd', 'Font', 'Data', 'zrCT' ...など、基本的になんでも構いません。コードの中ではこれらは通常文字列に使うダブル引用符("xxxx")ではなく、シングルク引用符('xxxx')を使うことに注意して下さい。また、あまり一般的ではないが、Creator IDとタイプは16進で表すことができ、'appl'は0x6170706Cとすることができる。

あるCreator IDと'appl'タイプを持つデータベースは、デバイス上で一つしか持つことが出来ません。通常これはPRCファイルで、プログラムの実行可能コードを表します。この特有の属性は他のタイプには適用されません。例えば、いくつかのDocファイルがPalmに入っていてPalm "Doc"リーダーを使う場合、creator/typeに'REAd'/'TEXt'を持つ多くのデータベースがデバイス上で存在できます。ですから、一つのデータベースを他から区別するには名前しかありません。全てのデータベースに付く名前は、creator/typeに関係なく異なっていなくてはなりません。例えば、Docに"videos"と名前を持っていると、NSBasicデータベースで同じ "videos"という名前を持つことはできません。

Palmランチャーは同じCreator IDを持つ複数のデータベースを特別に扱います。例えば、あるID('fBAR')の'appl'のタイプを持つデータベースを削除する時、同じIDを持つ全てのデータベースはデバイスから取り除かれます。これはアプリケーションを抜ける時、メモリ上に使わないデータが残るのを防ぎます。

PalmランチャーはIDによってデータベースをグループ化する為、特別なツール(PalmGearで探すことができるZarfCatalogなど)を使って各データベースが個別であることを確認できます。それぞれはバックアップディレクトリに別々に現われます。(バックアップビットがセットされていることが想定)特に不透明なオペレーションを使用して、それらを抽出する必要はありません。

アプリケーションとそのデータに同じIDを使わない唯一の理由は、なんらかの理由でアプリケーションが削除された後もデータをPDA上に残したい時です。

大きなデータベースへのアクセス

ヒープと呼ばれるメモリのエリアの限界の為、以前は約8000以上のレコードを持つデータベースを処理することが出来ませんでした。ヒープはデータベースヘッダーなどの情報を持っていて、このヘッダーは各レコードのハンドルを管理する大きな配列です。Palm OSのバージョン3.3までは、このヒープは64kです。おおまかに計算すると各レコードは最高でヒープの8バイトを使います。これによってデータベースは8000レコード以上になることはありませんでした。

これはOS3.5で変わりました。 以下のリンクはPalmOS 3.5の概要と新機能を説明したWebページです。

http://www.palmos.com/dev/tech/docs/palmos35/(英語)

特にここのパートがこのトピックに関するものです。

http://www.palmos.com/dev/tech/docs/palmos35/#The Dynamic Heap (英語)

要約すると、

ダイナミックヒープは、システムの使用可能メモリの量によってサイズが決まります。使用されるサイズは以下のようになります。
Device RAM size Heap size
< 2 mb of ram 64 k
>= 2 mb 128 k
>= 4 mb 256 k


これが意味するところは、Palm OS APIがサポートするデータベースなら最高で24000レコードまで持つことが出来ます。このレコード数になるとアクセスが遅くなりますが、処理できます。

現在Palmように出ているデータベース商品の多くは、独自のメモリ管理ファンクションを使って大きなデータ構造等を実現しています。従って、PDBではあるが、実際の内部はカスタム構造になっていると考えていいでしょう。さらに、例えば24000レコードのデータベースも、230k使わずに10〜40kのヒープサイズが可能です。

また、このレコードサイズを持つデータベースは、Hotsyncする時間に大きな影響を与えることも忘れないで下さい。

データベースプログラミングよく聞かれる質問と解答

Q: dbInsert (for keyed databases) と dbPut (for non-keyed databases)の違いはなんですか?

A: Michael Verive氏が以下の様に説明してくれました。

dbInsertファンクションは一つのレコードをキーを使うデータベースに書き込み、特有のキーを使う必要があります。このレコードの情報は与えたキーと共に保存されます。読み出される時、レコードはキーの順番で読まれます。従って、レコードをソート(整列)する必要がありません。dbPutファンクションは一つのレコードをdbPositionによって指定されたポジションに書き込みます。レコードは順番に読み出されるか、ポインターを指定するレコードのオフセットに移動することによって読み出されるので、キーを与える必要がありません。

データベースへアクセスするこれらの各方法は、共に有利/不利な点があります。キーを使うデータベースは各レコードに特有のキーが必要になるが、一つのレコードには一つのキーしか使えません。特にキーによるデータの検索やソートする時にとても便利です。キーを使わないデータベースは特有のキーが必要になりません。レコードへのアクセスはレコードとオフセットを指定するか、順番に読んでいくしかありません。特定のレコードを検索する場合、キーがない為により時間がかかりますが、データをソートすれば高スピードのバイナリ検索が可能です。全てのレコードが同じ長さを持つ場合、キーを使わないデータベースを使うと直接レコード番号を指定できるのでとても早くアクセスできます。もし早いレコードアクセスが必要でスペースの限度が問題でない(違う長さを持つ文字列をスペースを足しながらキーを使わないデータベースに保管するのはあまり効率がよくない)場合、キーを使わないデータベースが良いと思います。これには独自のソートおよびバイナリ検索のルーチンが必要になるかもしれません。

Q:どのフォーマットでPalmのdateを保存しているのですか?

A: NS Basic/Palmは (year-1900)*10000+month*100+day としてdateを内部保管しています。

Q: どのフォーマットでPalmのtimestampsを保存しているのですか?

A: h*10000+m*100+s です。

Q: NSBasic/Palmプログラムを実行して 'Bus Error' になるのはなぜですか?

A: POSEで "Bus Error" になるのは、dbInsertで使ったキーのタイプとは違うキーのタイプを使ってdbReadを試みた時が考えられます。

Q: Borland Delphiを使う場合、どのようにNSBasicデータベースにアクセスするのでしょうか?

A: バイパスを通して行うことを考えている場合は、Tabdee LtdのTurboSyncコンポーネントが使用できます。これらの製品はdateとtimeのフォーマットを、NSBasicが使うdateが保管されているフォーマットに変更してきています。詳しくはhttp://www.tabdee.ltd.uk(英語)をご覧下さい。

Q: データベースに最高でいくつのレコードが保存できますか?

A: このテクニカルノートの「大きなデータベースへのアクセス」セクションをご覧下さい

Q: キーを使ったデータベースでdbreadnextやdbreadprevを使用した時、ポジションを知ることはできますか?

A: (Mike Verive氏[mverive© peoplepc.com]から以下の情報を提供して頂きました。)

全てはどのようにレコードを取るかです。dbResetの後にdbReadNextを使う場合、レコード番号に合わせたポインターをアクティブにし続けることが出来ます。(ポインターを上げたり下げたりするだけなので, dbReadPrevも同様) このテクニックは(レコード数を確保すると共に)キーを使ったPhone DBで使用されています。

この時、レコードを削除したし挿入する場合は、ポインターを再度初期化しなければなりません。

キーを使ったデータベースを使って、どこに自分がいるのかを知る必要がある場合、他にもいくつかの方法があります。一つの方法として、配列を使ってキーとそのポジションを常に追い掛けることです。(これは2つ目のデータベースを使用しても出来ます) データベースの最初からdbresetを使って始め、キーの配列を一つづつ構築します。これによって配列のインデックスを使ってレコード番号を確定することが出来ます。削除/挿入の問題はここでもありますが、コードによって解決することができるでしょう。

Q: キーを使わないデータベースでレコードを削除することは出来ますか?

A: NS Basic/Palmには、キーを使わないデータベースのレコードを消すコマンドはありません。最適なアプローチは、「削除フラグ」フィールドをレコードに持つことでしょう。その後、「データベース圧縮」ルーチンを作成し、削除フラグがセットされているレコードを除きながら、データベースを書き直します。

Q: キーを使うデータベースではどのようにレコードを削除しますか?

A: dbDelete()ファンクションを使います。NS Basic/PalmハンドブックのDbOpenのページにある表をご覧になって、エラーの確認をして下さい。

Q: dbDelete()でレコードを削除した後dbGetNoRecs()を呼ぶと、削除前と同じ数のレコード数があるが、なぜ?

A: dbDelete()はレコードに「削除フラグ」をセットするだけです。dbClose()を使ってデータベースが閉じられると、削除フラグがセットされているレコードは自動的にデータベースから取り除かれます。

Q: メモリーカードは使えますか? (Springboard, Compact Flash, SD, Memory Stick)

A: デバイスによって異なりますが、NS Basicからこれらのメモリーが使えるものがあります。Fat Appとしてコンパイルすることによって、Runtimeと異なるメモリー領域にプログラムが置かれる時に起こる問題を避けることができます。現在のメモリーカードはVirtual File System (VFS)を使用していることを理解して下さい。この為、直接dbステートメントを使うことができません。NSBasicのVFS共有ライブラリを使うと、それらが有効になります。(TechNote23を参照して下さい)

InfoSyncのLarry Garfield氏による以下の記事は、VFSカードで出来ること、および出来ないことを説明しています。

データベースプログラミングのテクニック

どのようにMemo Padへ安全に書くか

Jeff Debrosse氏が、キー/キー無しの両方で、どのように「安全」にmemopadへ書くかを見せてくれています。さらにdate/timeのフォーマットコードも含まれています。

これをご覧下さい。 http://www.nsbasic.com/pub/palm/samples/Take-A-Memo.zip

リソースおよび関連情報

以下は.pdbおよび.prcファイルのフォーマットを解説するドキュメントです。

データベース変換ツール

CDK Palm's Conduit Development Kit. これまでにこれに関する良い報告を受けています。最新バージョンはVisual BasicからのコンジットのCOMをベースとした開発をサポートしています。
ConduitDB SDK BDEとODBCデータベースをPDBに変換。
Conduit Builder NSB Palmアプリケーション用にコンジットを構築するためのユーティリティ。
CPDB.net CPDB.netは、一式の同期ツールを伴ったPalmOS用の高速でパワフルなデータベースエンジンです。 CPDB.netを使うと、データベースをビジュアル化する機能を伴った、プロフェッショナルなアプリケーションを作成する時間を短縮できます。
CSV2PDB, PDB2CSV カンマで区切られたファイルをPDBへ変換、またはその逆
DagStudio DAG studioはNS Basicユーザ用に特別な機能を持つPDB変換ツールです。MEMOやBinary Stringsを含む全てのフィールドタイプを許容します。
Doc2PDB テキストデータベースをPalmのデータベースへ変換
Memo2PDB Memoファイルから.pdbファイルを作成
NSB Conduit Builder

NSB Conduit Builderは、NS Basic/Palm アプリケーション用のコンジットの構築と配付を容易にするユーティリティです。

PalmDB The PalmOS Database Automation Server (PalmDB)はCOMコンポーネントで、デスクトップ上に保管されているPalmOSデータベースの管理が行えます。
Par .pdb及び.prcファイルの作成および処理
PDA-tec この会社はNS Basicデータベースにアクセスできる様々な興味深いツールを提供。MDBやPDFファイル。また、初心者でも数行のスクリプトでコンジットが構築できる'Script Sync'もあります。
PDB Converter

文字列データのみのCSVとPDB間の読み/書き/変換を行う。Palm DB.DLL、Microsoft Hierarchical Flex Gridコントロール、およびPalm Conduit SDKのコンポーネントを使用(これらは含まれていないので、予めあなたのシステムにインストールされていることが必要)。By Michael Verive

PDB Converter Lite

Excel、タブ区切りのファイル、CSVを変換。VB6で書かれたソースファイルも含まれている。By Michael Verive

PDBDataManager

PDBDataManagerはCOMオブジェクトで、Access、Visual Basic、Delphiや他のCOMオブジェクトをサポートする開発ツールと一緒に使うことができます。これを使うと、DataSetのようなNS Basic PDBファイルが簡単に作成/読込みができます。マニュアルは英語で用意されています。使い方を示している多くのサンプルも用意されています。

PDB Manager .csvファイルのインポートおよびエキスポートを行う。2バイト文字のサポート。オプションで言語を英語に変更可能。
PDB Reader Palm Desktopバックアッププロセスの一部として作成されるPDBファイルを見ることができる
PDB PDBファイルを読解可能なテキスト形式に逆コンパイル
PDBC pdbcはソースファイルをPalm DataBase (PDB) ファイル、またはPalm Resource (PRC) ファイルにへ変換するコンパイラーです。pdbcに加え、PDB または PRC ファイルをpdbcソースファイルへ変換するディコンパイラー、pdbdec、があります。
PDBee デスクトップ上で、pdbおよびprcファイルの編集。
PDBingo! PDB Database Headerの分析と微調整。
pdb2Paradox

PDB Headerの作成、ParadoxデータベースからPDBファイルへのデータのエンコード(他のデータベースへの変更がより容易になる)、さらにPalmとPC間のtimeとdateフォーマットの変換を行うためのDelphiソースコード。

pdb2xml PalmデータベースからXML構文を作成
PDB-Def PDBファイルの作成および読込みのためのRapid Application Development (RAD)ツール
pInstaller Palmアプリケーションとコンジットソフトをインストールするためのインストーラ。
Quizzer .cvsファイルをPalmデータベースへ変換し、NS Basic/Palmを使ってアクセスする
RadNS NSBasicデータベースドリブン・アプリケーション用のRapid Application Development
RickPalmDB AccessデータベースをNSBasicで読めるPalmOS PDBファイルに変換するOCX
SimpleCOMMConduit.zip Simple COMM Conduit(VB6で作成)はPalmデータを受け取るためにMS Accessデータベースを使用。
TurboSync Delphi用のデータ変換ユーティリティ
VBToolKit PDBファイルを処理するデスクトップコントロールのセット
VFP - PDBClass デスクトップ上で、PDBへオブジェクトとしてアクセスするVisual FoxPro Class
Web Board 他のツールおよびサンプル
xmlMax

シンプルなXMLフォーマットでPalm上のデータベースを指定することによって、そのデータベースをアップロード/ダウンロードするコンジットおよびGUI。

 

さらなる情報

このトピックに関する便利なリソース等をご存じでしたら、または貴重な経験をお持ちでしたら、当社までご連絡下さい。support© nsbasic.com この情報ページを更新していきたいと思います。特にサンプルコードは大歓迎です。

注記:Palmはフォーマットを変更する権利を持っています。