レコード番号と参照番号を切り分ける作業

レコード番号と参照番号を切り分ける作業を続けているところだが,いよいよ正念場だ.リンクテーブルの構成は転々と変成してきたが,ようやく最終的なところまで煮詰まってきたと言える.最初から完全な設計からスタートしている訳ではないので,完璧に仕上げるためにはこの工程をやり抜かなくてはならない.要点を拾い出しておこう.

  1. 参照番号はカード氏名のハッシュ値によって決定され,リンクテーブルには参照番号(辞書順)でアクセスする
  2. カードを追加するときデフォルトのスロットが空いていないときは,上方に空スロットを見つけてその位置にリンクを配置する
  3. 外部からはリンク番号によってリンクテーブルに直接アクセスできない(リンクテーブルのリンク番号は公開されない)
  4. 基準ソートなどのテーブル並び替えではリンクテーブルは操作されない 
  5. 一覧画面の表示モードには①全カード,②現表示カード,③部分図カードの3つがある.これらは,①lookup,②ActiveList,③PartialListの3種のテーブルで管理されるが,①にはリンクテーブルのレコード番号が格納され,②,③には参照番号が格納されているという違いがある.外部からはこれらのテーブルのレコード番号によってアクセスする
  6. ActiveList,PartialList などからlookupで参照番号から逆引きするのは不利なので,参照番号でアクセスするための関数を整備する
  7. UNDOではリンクテーブルとlookupテーブルはバックアップされない ActiveList,PartialListは必要に応じバックアップされる

とりあえず,問題のUpdateRecordを精査してみよう.TOPOLOGY:UpdateRecordという関数は呼び出されるたびに,updateflagがオンになっているカードのレコードを1件づつ送付するという関数で,VBのGetNewTableではビルドフラグがオンのとき実行されるBuildNewTableの場合を除き,つねにUpdateTable→ UpdateRecordが実行される.セッションの開始は,リンクテーブルが管理しているupdateindexという値がゼロになっていることにより判別される.処理が何らかのトラブルで中断した場合などのリカバリの手続きが存在しない,などの弱点はあるものの,まぁ,それほど致命的な欠陥とも言えない.

TOPOLOGY::UpdateRecordの内部ではモードごとに処理を切り替えているが,実行関数はTOPOLOGY::UpdateRecord(別バージョン)で統一的に扱われている.この関数はかなり疑わしいところがある.とりあえず,関数名を変更しておこう.⇒UpdateRecordFuncにリネーム.

▲コラッツ特注版では基準ノードを変えることができないようになっているのだろうか?系統並び替えしても図面が変化しない.⇒いや,動いた.タイミングの問題だろうか?⇒少なくともダブルクリックでは作動しない.⇒いずれにせよ,思ったような動作になっていない.どこかでトンネルが崩落しているような感じだ.DLLの系統並び替えには値は渡っているのだが… いや,もしかすると,描画が遅いだけかもしれない.⇒どうも,そういうことのようだ.

▲一覧画面のカード数表示で,選択されたカード xxx/yyy のように表示されるが,yyyは一覧画面に表示されているカードで,xxxの示す選択されたカード数は,yyyの中に含まれるとは限らない.表示の仕方に問題があるのか,選択されたカードのカウントに問題があるのか?選択操作は一覧画面の範囲とは連動していなので,別の表示を考えた方がよい.

UpdateRecordFuncの最大の弱点は,更新レコードをグローバルに管理しているため,リンクテーブルにアクセスするしかないという点にある.実際には個別のテーブルにアクセスして参照番号を取り出しているが,リンクテーブルのレコード番号で個別テーブルにアクセスするというイレギュラーな構成になっている.しかも,lookupには参照番号ではなくレコード番号が格納されているという違いも見落とされている.

UpdateRecordFuncで一括処理するのではなく,個別テーブルごとに関数を立てるということも考えられるが,updatecountをどうするか?などの問題も残る.1件分のレコードを生成する関数MakeRecordは共通なので,そのカードが現在のテーブルに載っているか否かを確認できれば,それでよいのではないだろうか?個別テーブルで参照番号を検索するのはコストが掛かってしまうが,それしかないのではないだろうか?

明らかに現行方式は(一見したところ動作しているようには見えるが)間違っているので,修正が必要であることは明らかだ.「個別テーブルで参照番号を検索」するコスト増を回避するとすれば,テーブルごとにupdateindexを管理するしかないと思われる.3つのテーブルはすべて同じlongtableクラスなので,メンバーにupdateindexを追加してやればほぼ同等の処理になる.UpdateRecordFuncには引数でlong *tableを渡しているが,longtable *tableに書き直すのは容易だ.それがベストかもしれない.⇒実装してみよう.⇒実装完了した.少し動作確認が必要だが,どうすればよいか?CARDLINK->updateflagが立たないと動作しないので,データをいじらなくてはならない.

@3077というカードの所属に書き込みしたところ,FAMILYTREE:callSendCardで (_pagenum < 0)というエラーが出た.現親ページ番号が入っているpagenumが負値になっている.oyapageは実親登録なしではNOPARENTS=-1が入ることはあるのだが… エラーを無視して続行して,MakingLookUpで停止した.CommandStartFlagが立っているためだ.CARDTABLE::makelinkの出口でMakingLookUpが実行されている.コマンド処理の出口では必ずMakingLookUpを実行しているので,ここでは実行しなくてもよいのではないかと思われるのだが…

その意味ではUNDO動作中にlookupにアクセスすることがあるとすれば,それはイレギュラーな動作になると考えられる.チェックできるだろうか?callSendCardでは出口でもMakingLookUpを実行している.callSendCardではCommandStartをどこで呼び出しているのだろう?MakingLookUpを呼び出している箇所はかなりある.

  1. FAMILYTREE::callSendCard
  2. MARGTABLE::deletelink
  3. CARDTABLE::deletelink
  4. MARGTABLE::makenewlink
  5. CARDTABLE::makelink
  6. FAMILYTREE::AppendFile
  7. LINKTABLE::ImportEnd
  8. SetCardData
  9. COUPLING::OpenFamilyBase
  10. COUPLING::InitLinkTable
  11. UNDOSYSTEM::UndoProcess
  12. UNDOSYSTEM::CommandEnd
  13. UNDOSYSTEM::CommandEnd

最終的にはMakingLookUpを廃止して,常時lookupをリンクテーブルと同期させておくようにしたいと考えているのだが,いまのところは必要な処理だ.⇒この問題はいまのところ保留して,停止しないようにしておこう.しかし,このアサーションには修正日付が付いていないので,元々こういう動作になっていたのではないかという気もするのだが… コラッツ問題をやっている間は,ファイルの読み書きしかやっていなかったので,穴が空いてしまったのだろう.

pagenum = NOPARENTS を実行しているところは無数にある.従って,このパラメータが負値を取るのはエラーではない.おそらく,何らかの一時的なトラップのつもりで置いたIF文を恒久化してしまったのだろう.とりあえず,oyapageがNOPARENTSのときは,停止しないようにしておこう.⇒論理は動作しているが,updateflagの様子が少しおかしい.編集したカード以外の無数のカードにフラグが立っている.PDB()->updatecountが300を超えている.このフラグはCARDLINK::updateflagupで立てるようになっている.

どこから呼び出しているのか判定できるように引数を追加してみた.⇒呼び出し箇所は14箇所もある.⇒こともあろうに,MakeCouplingでフラグを立てている.これはいくらなんでもやり過ぎではないか?⇒いや,MakeCouplingというのは2つのノードの婚姻手続きなので正当だ.しかし,このフラグはファイルがロードされた時点でリセットされなくてはならない.ないし,レコードが取り出された時点でクリアされるべきだろう.TOPOLOGY::GetRecordではMakeRecordを実行後にリセットしているが,MakeRecordでリセットすべきだろう.

しかし,それにも問題がある.MakeRecordという関数は参照番号を引数としてレコードを生成しているため,カードリンクに直接アクセスできない.MakeRecordはCOUPLINGの関数なので,COUPLINGに専用ルーチンを新設してMakeRecordと並行に呼び出すことにしておこう.

PDBのうupdateflagは計数型なので,updatecountは単なるデクリメントでなくカウントを減じるようにしておく必要がある.また,この値が負にならないことを確認する.⇒CARDTABLE::clearupdateflagという関数もあった.使われているのだろうか?2箇所で使われている.TOPOLOGY::UpdateRecordFuncの末尾とFAMILYTREE::cancelupdateだ.cancelUpdateは外部呼び出しがあるだけだ.それぞれ cleanUpdateflag,cancelUpdateにリネームした.

▲CARDLINKのupdateflagとPDBのupdatecountが同期していない.⇒CARDLINK::updateflagupはフラグが立っていないときしかインクリメントしていない.updateflagとupdatecountはCARDLINK:updateflagupの中でしか操作されていないので,完全に同期するはずだ.⇒まだ,どこか余分なところでフラグを上げているようだ.⇒NAMESORT:SortNameSubで立てている.テーブル並び替えでスワップされるとインクリメントするようになっている.

その必要はあるのだろうか?⇒CommandEnd→ RebuildCardListでソートしている.「カードテーブルを現在のソート基準に従ってソートする」という理由による.悪いアイディアではないとは思われるが…仮にソートしたとしても,全カードが移動するようなスワップになるというのは考えづらい.⇒従来,基準ソートで降順の場合には,最初に昇順でソートした後,降順で並べ直したが,参照番号が永続コードになったので不要な措置となっているはずだ.

どうもリンクテーブルとlookupの関係がうまく調整されていないのではないか?リンクテーブルからMakingLookUpしたとすれば,せっかく保持していた並びが崩れて元の黙阿弥に戻ってしまう.これは完全な誤りと思われる.ちょっと頭を冷やす必要がありそうだ.lookupというのは,①リンクテーブルの有効なレコードすべてを含む,②つねにソートされた状態で維持される,という二つの特性があると考えられる.従って,MakingLookUpでリンクテーブルから再構築するという操作は完全に誤りというしかない.リンクテーブルでは原則として,①生成,②削除以外の操作は行われないと考えてよいので,これらのイベントが発生した時点でlookupの時点処理を行う以外にない.

①生成の場合には,テーブルへの挿入,②削除の場合にはテーブルの前詰めが必要になる.ややコスト過剰だが,仕方ないだろう.現物のリンクは移動しないから,挿入は末尾に追加でもよいのだが… つねにソートを通すということであれば,テーブルは穴空きのまま運用し,ソート時に前詰めする処理を組み込むということも考えられる.この方がトータルのコストを削減することができるのではないか?こうなると,lookupというより,むしろ「ソートテーブル」と呼んだ方が間違いない.

まず,最初にMakingLookUpを止めるところから始めよう.と言っても,少なくとも一度それを生成しなくてはならない.どこでそれをやればよいか?現行ではどうなっているのかを見ておこう.InitLinkTableのステージ7だ.CheckDataLinkを引数空の実行モードで呼び出して,データの不整合を補正した後,NameSortを呼び出して最初のテーブル並び替えを実行する前に実行される.NameSortを実行するためにはlookupの存在が不可欠だから,もっとも適切な場所と言えるだろう.それ以外の場所における実行はすべて止めることにする.

新規ファイルの場合,InitLinkTableは実行されない?⇒InitLinkTableを通っているのだが,事後にもう一度OpenFamilyBaseの中で実行される.これは,「登録レコード数0」で新規カードを登録した後の措置だ.⇒特にこれで問題があるという訳ではないので,とりあえず,これでよしとしておこう.ファイルオープンのどこかで事故ってカード数ゼロとなることはあり得るので,事後に新規カード追加というのでよいと思う.ここではカードテーブルのlookupしか初期化していないが,これでよいだろうか?結婚テーブルも初期化した方が安全なのではないか?

▲カードの肩書を変更→ 登録でFAMILYTREE::CallSendCard→ CommandEnd→ TopologicalSortのとき,PARTIALNAME:ExtractPartialCardでif (PartialList && valid != PartialList->count)のエラーが発生した.valid=279 <> PartialList->count=278で1の差が出ている.なぜだろう?この関数ExtractPartialCardはリンクテーブルを直接参照している.⇒これまでは,MakingLookUpの中で部分図リストを更新していたのではないだろうか?

いや,それにしてもおかしい.行われた操作は肩書の一部を書き換えただけであり,カード削除など部分図の構成に影響するような操作は行っていない.⇒CARDTABLE::MakingLookUpでは基本クラスのMakingLookUpを呼び出しているだけだ.CallSendCardの操作に何かやり損なっている点があるのではないか?

PartialListはPARTIALNAMEクラスのメンバーだ.PARTIALNAMEを持っているのはFAMILYTREE.longtableに関してはaddという関数は作ってある.⇒一箇所漏れがあったので修正した.PartialListはPARTIALNAME::MakePartialListで生成される.部分図リストというのは,おそらく,メンテナンスしないでつねに,生成・使用されるものなのではないだろうか?部分図の操作はすべて「部分図コマンド」として実行される.⇒MakePartialListという関数は呼ばれていない.

PartialListは,TOPOLOGY::RebuildCardListとPARTIALNAME:SelectPartialMapで生成される.⇒RebuildCardListの呼び出しは3箇所あったが,いずれも2018-02-12に廃止されている.従って,現行ではSelectPartialMapが唯一の生成関数ということになる.この関数は部分図コマンドの実行時,およびUndoProcessから呼び出されている.いや,見落としていた.RebuildCardListはUndoProcessとCommandEndからも呼び出されている.

InitLinkTableで既定の部分図がPartialLoadで読み込まれている.このときのカウントは278だ.これが279に増えているということはどこかで部分図に属するカードが増えていることになる.カード登録のCommandEndでRebuildCardListが実行され,その後,UndoProcess出口のTopologicalSort→ ExtractPartialCardで不一致が発現する.RebuildCardListではlookupを経由してアクセスしているのに対し,ExtractPartialCardは直接リンクテーブルにアクセスしているという違いがある.どうも,リンクテーブルとlookupに齟齬が生じているようだ.ExtractPartialCardの論理をlookupベースに変更すると,この不一致は解消するが,今度はMakeActiveListで同種のエラーが生じる.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA