レコード番号と参照番号の切り分けをクリア

レコード番号と参照番号の切り分けという課題はほぼクリアできたのではないかと思う.2021年の末にコラッツ対応版を初めた時期に,「コラッツ対応版応急措置」というのを何件か入れている.この辺りが今どうなっているのかを調べてみよう.作業を開始したのは,2021/12/11だ.この頃は,コラッツ木サンプルのデータ入力をやっている.発生していたエラーには以下のようなものがある.

  1. 先祖ノード絶対世代番号不一致
  2. LINKTABLE::MargTakeChild 結婚リンクが消えていない!⇒このエラーは,子ども氏名を消去した時に発生する
  3. 選択状態が残ってしまう,他のカードを選択しても消えない
  4. ページ設定パネルが開けない,印刷もできない
  5. 先祖並び替えが動作しない,ボタンが効かない,ドラッグ移動もできない
  6. インポート・エクスポートが動作しているバージョン:Ver 2.2.0.030 Rel 2021-03-17
  7. 最終版からリスタート,2021-04-10
  8. 氏名欄で左右カーソルキーを押すと入力文字が消える
  9. カード画面上部ツールバーの氏名欄で名前の後ろに余分な空白が入る
  10. カード数1000点のコラッツ木のインポートに時間が掛かる,1点処理するのに25秒掛かっている
  11. CARDLINK::makelinkで時間が掛かっている,UNDOは止めておく
  12. ZTYW処理が作動している,暫定的にgoodsonを空としている
  13. 人名枠高の計算がおかしい,フォントサイズを大きくすると人名枠が世代境界を超えてしまう
  14. コラッツ1000.zelを開くのにかなりの時間が掛かり,マネージド・デバッグ・アシスタントが作動して停止する
  15. 親の垂線が兄弟連結線から突き出ている
  16. TOPOLOGY::ClearTableで参照番号不整合が発生したとき ⇒ すでに止めてあるが,「コラッツ対応版応急措置@20211219」というリテラルで明示する
  17. NAMEBOX::Drawで人名枠高が世代高を超えた ⇒ コラッツ対応版応急措置@20211219」で止める
  18. MARGBOX::makeGoodSonの冒頭で「つねに中吊りする@20211216」でゼロ復帰 ⇒ コラッツ対応版応急措置@20211219」で対処
  19. TRIBEBOX::CheckAbsoluteGenerationで系列優先ノードのbreakupがオフという理由でエラーが起きている.「コラッツ対応版応急措置@20211223」で止めておく
  20. 基準番号を1~1000に振り直すことにした
  21. 垂直親子連結線が兄弟連結水平線を突き出ている.

Collatz12146.ZELというサンプルを開いてみた.名前の通り,12146点のデータが登録されている.系統並び替えだけ700秒掛かっているので,全体では15分くらい掛かったのではないだろうか?上の方でかなりひどいスプリットが起きている.多分,これが「極小反例の自動生成」ということに取り組むことになった原因だろう.

一番気がかりなところでもあるので,まず,これをシューティングしてしまいたいところだが,その前に,「コラッツ対応版応急措置@20211219」と「コラッツ対応版応急措置@20211223」を解除するところから始めよう.今後の作業の目標は,「コラッツ特注版をノーマライズして公開版に向けて統合する」ことだ.

コンパイルエラーが出ている.CARDDATA::refnumが存在しないというエラーだ.CARDTABLE::tablesizeという値でも不在が起きている.前者は多分「setRefnumはCARDBASEで実行する@20230107」に関係しているものだろう.暫定的にrefnumをRefnumに変更している.後者はBASETABLE::tablesizeを暫定的にtablesize2としためだ.元に戻しておこう

KAKEIZU::getmarriageでは参照番号を指定してデータの取り出しを実行している.レコード番号でアクセスできるようにした方がよい.⇒KAKEIZU::getmarriage/getcarddataでは,num ないし refnum という語を使っているが,これは参照番号ではなくデータベースのレコード番号である.参照番号はデータベース内部では「登録番号」と呼ばれている.紛らわしいので,引数をrecnのように書き換えておこう.

KAKEIZUのメンバー関数の中でnumないしrefnumを引数とする別の関数がある.これらもチェックしておこう.QUICKDBのcurnumもcurrecnに改めておく.savemarriage,savecarddata,makemargfile@もrecnに変更した.



Renumberではリンクを移動しない

参照番号のハッシュ化は,コラッツ対応修正により,登録カード数の上限が1000のオーダーから10000を超えるようになったことを直接のきっかけとしている.しかし,そのことによって,「リンクがどこに置かれていてもまったく問題なく動作する」というNシステムの最大の特長が著しく損なわれるか,ないし制約を受けるようになった.テーブルにハッシュでアクセスするためには,そのレコードはテーブルの固定位置に配置されることが前提となる.それと,Nシステムの「リンク配置の自由度」は明らかに背反している.

参照番号のハッシュ化が後戻りのできない決定であるとすれば,どこかで折り合いを付けなくてはならない.参照番号のハッシュ化はゼルコバの木の最大構成要素である人名テーブルと結婚テーブルに関わるものではあるが,それ以外の要素ではこれまでと同様,配置の自由度は損なわれてはいない.テーブル並び替えというのは一覧画面出力に関わるものであり,人名/結婚テーブルのレコード並びとは独立であるはずなので,人名/結婚テーブル上のリンクが固定位置にあったとしても,実行可能であるはずだ.⇒どうもlookupテーブルを作り損なっているようだ.⇒NAMESORT::SortNameSubで「lookupを前詰めする@20230113」の論理にミスがあった.⇒修正して動作するようになった.

基準ソートの実行で選択カード数不一致が発生する.TOPOLOGY:RebuildCardListではUndoStartFlagがオンのときには選択リストから選択フラグを再設定し,オフのときにはフラグから選択リストを再構築している.どちらの場合も,フラグのカウントとリストのカウントは一致しなくてはならないのだが… リナンバーを実施した場合には,選択リストは無効にしなくてはならない.⇒いや,そういう動作になっているはずだ.⇒CARDTABLE::Renumberが間違っているのではないか?lookupに格納しているレコード番号を書き直している.⇒「Renumberではリンクを移動しない@20230116」で収まったようだ.

58点削除→ 基準ソート→ Undo→ Undoで大量のエラーが発生する.親リンク不整合,カード参照番号の重複,子インデックスが負,親番号不一致などの後,どこかで例外を起こしている.⇒MARGLINK:CheckMargLinkのエラー出力に誤りがあった.

カード削除単独,ないし,他のタイプのソートとの組み合わせではエラーは生じないので,原因が基準ソートにあることは明らかだ.おそらく,それもリナンバーしていることが最大の原因と思われる.⇒確かにそのようだ.ということは,リナンバーをUndoしたとき,元の状態に戻っていないということなのだろう.

リナンバーでは「リンクを移動しない」ことになっているから,変化しているのはカードの参照番号だけだ.⇒カードの参照番号はCardNumListのリストアで復元できているはずなのだが,参照番号を参照しているすべてのリストが復元されているかどうかが問題だ.いや,そもそもCardNumListが保全されていないのではないか?

UNDOSYSTEM::BackupPointDataのSORTREFNUMにSetUndoListを実行するようにしたが,エラーは収まらない.CardNumListを生成しているタイミングが悪いのではないか?⇒確かにそのようだ.⇒NAMESORT::NameNarabekaeの入口でBuildNumListを実行するようにして解決した.多分これで,ほぼ問題は解決したのではないかと思う.まだ,何か見落としがあるだろうか?


参照番号のハッシュ化とリンク移動の自由は相反する

昨日の修正では,基準ソートでRenumberを実行し,その代わりにUNDOをリセットするという作りになっているが,基準ソート時にlookupを保全しておけば,UNDOできるのではないだろうか?仕様的にはまるごと廃止という方向に向かっているところだが,試しておいてもよいのではないか?⇒lookupでは復元できない.lookupにはリンクテーブルのレコード番号しか入っていないから,参照番号を保全したことにはならない.もし,やるのであればやはり,CardNumListを生成・保全するしかない.

仕様的には参照番号のリナンバーというのは廃止で決定なのではないか?もし,なんらかの理由でリナンバーする必要が出てきたら,その場合には,内部処理として独立に再構築する以外ないと思う.ただし,アプリとのインタフェース上参照番号は不可欠であり,一覧表のテーブルにその情報が記載されていなければそもそも動作しない.ただし,それを公開情報として表に出さないというだけだ.あと腐れのないように実装しておこう.⇒いや,CardNumListを復活させて,基準ソートでもUNDOできることを確認しておくべきだ.

仕様がぐらついているが,CardNumListの操作を復活させた.これで一応基準ソートを実施してもUNDOできるようになったが,エラーが発生している.カード削除とテーブルソートを複数回実行→ Undo→…でRestoreShadowのエラーが起きた.CARDTABLE::InsertLinkで例外が発生している.再現できるだろうか?

カードをブロック削除→ 基準ソート→ 氏名でソート→ 肩書でソート→ Undo→ Undo→ Undo→ Undo→ で再現した.カード削除の復元に失敗している.リンクを戻そうとした位置に先住者がいたためだ.recn=243.RestoreShadowではshadowの親番からレコード番号を割り出している.この操作は(元の状態を完全に復元するのだとすれば)正しいと思われるのだが… ブロック削除→ 基準ソート→ Undo→ Undoで再現できる.基準ソートの動作自体おかしいところがある.ソートしただけで,系図画面に表示されているカードが減ってしまう.設定は「親族図:直系血族図」で一覧画面はすべてのカードだ.

全カード数=307のうち,親族図には278点が含まれる.このうち58点を選択して削除すると,画面には37/249点のカードが残る.選択したカードはすべて親族図に含まれるものだから,278-58=220点が残りそうに思われるが,37点まで減ってしまうというのは,「直系血族図」であるためと考えられので,間違ってはいないはずだ.これを並び替えても表示カード数は変化しないが,Undoで7点にまで減ってしまう.これはかなりおかしい.基準ソートのUndoでやり損なっているのだろう.

これは,おそらく基準カードが変化してしまったためと思われる.⇒全体図ではこの事象は起きない.ただし,InsertLinkのエラー(先住者の存在)は発生する.レコード番号は246.⇒Renumberの中でリンクを移動(前詰め)していた.⇒Renumberではリンクの移動は行わないように修正した.これで一応解決したはずだが,エラーはまだ続く.

▲ブロック削除→ 基準ソート→ Undodeで,TopologicalSort→ MakeActiveListを実行して,(treeview->validcard != ActiveList->count)のエラーになった.validcard=249に対し,countは198のままだ.フェーズはDECOMPOSITIONなので,すでにフィルタリングは完了しているはずだ.⇒PDBのlookupのカウントが198しかない.これはかなりまずい.いや,こちらの数字の方が正しいのではないか?PDBのrecordcountは249になっている.なぜだろう?

元々のカード数は307点だ.それから58点削除しているので,249点でなくてはならない.198というのはどこで起きているのか?基準ソートの入口では249になっている.⇒RebuildCardListで変化しているようだ.UNDOSYSTEM::CommandEnd→ RebuildCardListで変化している.RebuildCardListの中でもう一度NameSortが呼び出され,その中でcountの変化が起こっている.ソートは_MAXITEMモードで実行されている.⇒確かにlookupを前詰めする@20230113という処理が入っている.これはカード削除などでできた空欄を前詰めするための処理だが,誤動作しているようだ.

どうも,かなりまずい設計になっているような気がする.というか,元々はそういう設計になっていたのだろう.参照番号=レコード番号という設計だ.確かに,リンクを移動しないで参照番号の付け替えだけを実施すれば,参照番号〰レコード番号という前提が崩れてしまうのは当然だ.やはり,参照番号のリナンバーというのはバッドアイディアというしかないような気がする.現行では,参照番号というより,ハッシュ値というべきものに変化してきているので,それに対応して変化するしかないのではないだろうか?

参照番号をハッシュ値として扱うというのはそれ自体は悪い発想ではないが,逆にNシステムの本来の思想である,リンクがどこに移動しても問題なくアクセスできるという理念からはかなり大きな後退であるような気もする.この2つを両立させることは不可能なのだろうか?

参照番号を永久コード化する@20230109

▲Undo処理中にRestoreShadow→ InsertLinkでエラーが出た.挿入しようとしている位置にすでにリンクが存在しているというエラーだ.⇒再現しないが,別のエラーが出た.

linkの参照番号は@233でこれからrecnの233が計算されたものと推定されるが,その場所には@233の別のカードが存在している.削除されたカードの参照番号は基準ソート以前のものと推定される.この状況で間違っているのはどちらのリンクと言えるか?

UNDOSYSTEM::RestoreShadowでは,レコード番号をshadowの親番から取っている.これは正しいのではないか?基準ソートのUndoが正しく動作していない可能性が考えられる.⇒従前では,NAMESORTはCardNumListとMargNumListを保持していたが,現行では完全に抹消されている.これらのリストには有効な参照番号の写しが格納されている.これは必要だったのではないだろうか?⇒確かに基準ソートでリナンバーしなければこのエラーは発生しない.

カードを70点選択→ 単点削除→ 単点削除→ 単点削除→一括削除→ 基準ソート→ Undoを実行して,UpdateCardBaseで基準ノード不在エラー(!basenum && CHARTTYPE != DISP_IMPORT)が起きた.⇒*basecardは0だが,*basepartには237が入っている.表示モードは部分図だ.呼び出し元のmZelkova::mUpdateBaseRefnumでは,BaseDispAll=3079,BasePartial=134が入り,それを引数で渡している.「参照番号が一致しない場合は更新とする@20180129」という論理で*basecard = 0に変わっている.⇒エラーを無視して続行で,上のエラーが再現した.

完全に抹消してしまっているので,かなり大変だが,CardNumListを復活させておこう.BuildNumListという関数も必要だ.「参照番号を永久コード化する@20230109」というオプションでパージした部分だ.⇒「参照番号を永久コード化する」というのは覆されてはならない決定である.抹消したコードを復活させることは不可能ではないが,必ずしもベストソリューションとは言えないのではないか?むしろ,この方向を突き詰めて,「基準ソートを実行しない」,「参照番号をユーザから隠蔽する」という方向に進むべきなのではないか?

基準ソートを実行すると,参照番号が1から始まる通し番号になるというのは悪くないが,たとえば,結婚参照番号はまったくユーザに公開されていないが,それによる不都合は基本的に存在しない.もし,仮にカードの参照番号をソートするのであれば,その時点ですべてのUNDOを一旦破棄せざるを得ないだろう.そこまでして,実現するだけの意味があるだろうか?基準ソートを実行するか否かは今のところペンディング事項だが,それを実行した場合にはUNDOをリセットするという作りにしておいた方が安全だと思う.⇒一応動作チェックしておこう.

基準ソート→ Renumberを実行→ UNDOリセットを実装して,基準ソートを繰り返したところ,CARDTABLE::Renumberで(max && max == maxrefnum)のエラーになった.maxrefnumは基本的に単調に増加すべきものと考えられるので,同じ値をセットする操作はイレギュラー動作とみなされる.しかし,Renumberは参照番号の振り直しなので,そうなることは避けられない.⇒Renumber時には,maxrefnumをリセットしておけばよいのではないか?⇒解決

部分図から全体図に切り替えて,どこかでハングしてしまった.⇒かなりまずいことになった.系統並び替え→ 三極検定→ 極大セグメント検定でハングしている.maxloopが302という大きな数字になっている.REDLINEの値は50だ.⇒どうも,UNDOのインタフェースが壊れてしまったようだ.VB側で,LineageSort→ ChangeBaseRefnum→ UpDating→ UndoStatus→ RefreshSub→ LineageSortで無限ループに陥っている.ChangeBaseRefnumで誤動作しているようだ.

Z.BaseRefnumが3079なのに対し,Zelkova1.TopologicalSortが171という数を返しているためだ.画面は部分図になっているのに,DispPartialMapがFALSEになっている.⇒DispPartialMapを強制的にオンにしたら,抜けてきたが,エラーパネルが出ている.

image

画面はいつの間にか全体図になっている.カード画面は「系図画面上のカード」で,全体図なら300が正しい.部分図なら271のはずだ.どうも,かなり話がこじれてしまったような感じだが,再現できるだろうか?⇒基準ソートとおそらくそれに伴うUNDOリセットの影響と思われる.起動→ 基準ソート→ 部分図⇒全体図で再現できる.UNDOチェーンのリセットだけでそれほどの影響が出るというのはほとんど考えられないのだが… 基準ソート実行後は,VBで基準カードの切り替えを行っているが,それほど致命的な操作とは思えない.戻り値の参照番号は176だ.障害は部分図⇒全体図の切り替えで起こるので,どこかで何かしらの齟齬が発生しているものと思われる.基準ソートを実施していなければ,この切り替えは問題なく通る.

何かパラメータが落ちているのだろうか?このエラーは初回限りで,別の操作を行ったあとの,基準ソート→ 部分図⇒全体図では(操作を反復しても),起きない.⇒ChangeBaseRefnumの出口でやっていた操作(Z.BaseRefnum,Z.BasePartial,Z.BaseDispAllの更新)をUpDatingの前に実行するようにして解決した.

TOPOLOGY::GetRecordを包括的に使うように修正した.

  1. 修正した関数:
  2. CARDTABLE::GetCardBaseRecord
  3. FAMILYTREE::GetRecord
  4. TOPOLOGY::UpdateRecordFunc
  5. TOPOLOGY::GetActiveRecord ⇒ 廃止
  6. PARTIALNAME::GetPartialRecord ⇒ 廃止
  7. FAMILYTREE::GetAllRecord ⇒ 廃止

▲テーブル並び替えでは「現ソート列」を管理しているが,Undo/Redoしたとき,アプリに伝達されていない.


Your Daily Epsilon of Math を毎日更新

Your Daily Epsilon of Math を毎日更新するのに結構時間が掛かっている.始業時バックアップの日付が0時を回って翌日の日付になってしまうようになった.もう,これが常態と認めるしかないだろう.参照番号とレコード番号の切り分けというのは,一通り収まったのではないかと思う.何か抜けているところはあるだろうか?とりあえず,フィックスできるところをすべてフィックスしてしまうことにしよう.「仮修正」が20箇所ある.⇒ここで,もう一度バックアップを取っておこう.

  1. lookupを前詰めする@20230113
  2. maxRecordではmaxrecordを返す@20230113
  3. lookupを自動更新する@20230112
  4. MakePartialListを廃止する@20230111
  5. lookupテーブルは一度だけ生成される@20230111
  6. MakeRecordでupdateflagをリセット@20230110
  7. UpdateRecordFuncの改造@20230110
  8. UNDOでリンクテーブルを保全しない@20230109
  9. CallGetRecordNumをlookup対応に変更@20230110
  10. SendUpdateDataの引数仕様変更@20230110
  11. 参照番号を永久コード化する@20230109

ここで一度バックアップを取り直す.

▲リンクテーブル全体にアクセスするとき,ダイレクトにアクセスするのと,lookupを経由するのとどちらが効率的か?

結婚テーブルのlookupの操作を確認する.⇒結婚リンクテーブルでも,lookupは生成されているが,有意な使い方はされていないのではないだろうか?実際,MARGTABLEではCARDTABLEと異なり,lookupの更新さえ行われていない.結婚テーブルからlookupを完全に排除してもよいのではないかと思うのだが…⇒MARGTABLE::MakingLookUpを完全にNOPにしても問題なく動作している.

lookup自体は基本クラスのBASETABLEのコンストラクタで生成しているので,作られてしまうのは避けられないが… MARGTABLE::MakingLookUpという関数は廃絶しても問題ないと思う.MARGTABLE::Renumberというのも不用なのではないか?⇒MARGTABLE::Renumberを廃止した上で,基準ソート時のRenumberを復活させてみよう.⇒FAMILYTREE::AppendFileではMARGTABLE::Renumberを使っている.これは必要なのではないか?ただし,この呼出には,「この論理はチェックを要する @@2017-09-22 」という書き込みがある…

基準ソート時にリンバーするというのは悪い仕様ではないので,復活させるのが妥当と思われるが,結婚テーブルではlookupを使わないという方針なので,もし,AppendFileで結婚参照番号のリナンバーが必要であるということになるとすれば,作り変える必要がある.ただし,アペンドした時点で重複しない参照番号が発行されているとすれば,あえてリナンバーする必要もないようにも思われる.この点に関しては現時点では保留としておく.

▲TOPOLOGY::GetRecordはlookup未対応@20230110なのではないか?⇒GetRecordに引数で渡されるlong* tableには2つのタイプがある.いずれもlong配列だが,lookupはレコード番号,他のテーブルは参照番号なので同一に操作することはできない.⇒これに対処するためには,TOPOLOGY::GetRecordの引数を仕様変更して,long*ではなく,longtable*を受け渡しするようにするしかない.⇒GetRecordの引数の仕様を変更したが,コンパイルエラーになったのは,TOPOLOGY::GetActiveRecordだけだ.

部分図やlookupでは何を使っているのだろう?COUPLING::MakeRecordを使っているのかもしれない.MakeRecordはGetRecordでも使われている.MakeRecordを呼び出している箇所をすべてGetRecordに統一するとよいのではないだろうか?

ようやく波に乗れたような気がする

ようやく波に乗れたような気がする.ともかく,lookupの更新を組み込んでおこう.⇒実装した.大体うまく動作しているように見える.テーブル並び替えは掛かってこないが,ソートは実行されている.なぜだろう?TOPOLOGY::RebuildCardListの中でNameSortが実行されている.⇒これでよい.昨日のエラーを人為的は発生させて動作を見ることにしよう.⇒新規カードはエラーなしで作れたがその後が悪い.いや,これは,わざとエラーを発生しているのだから,当然だ.

主なポイントは実装できたと思う.あとは,関数名などの整理だけだ.getmaxrecnではリンクテーブルサイズを返しているが,maxrecordを返すように変更できる.いや,何か失敗しているのではないだろうか?図面種別部分図で,1ブロックを一括削除した後,氏名でソートを2回繰り返すと表示範囲がまったく変わってしまう.一覧表の表示モードは<すべてのカード>となっているが,4点しか表示されていない.すべてのカードであれば,208点表示されなくてはならないのだが…

カードを70点一括削除して一覧画面の表示がおかしくなる.208点表示されなくてはならないところ,4点になり,氏名でソートするとその内容が変化してしまう.CommandEndの出口でNameSortを実行した時点でlookupのカウントは307のままだ.NAMESORT::SortNameSubには空欄を前詰めするという機構が備わっていない.⇒対処した.

getmaxrecnはmaxrecordを返すように仕様変更しているが,正しい使用法になっているかどうかをチェックしておこう.getmaxrecnは189箇所から参照されている.すべてforループのカウンタとしてつかわれていると見て間違いないだろう.⇒一応問題なさそうなので,予定通りmaxRecordにリネームしておこう.⇒その前に,バックアップ.

COUPLING::resetUpdateflagで(PDB()->updatecount < 0)になった.CARDLINKのupdateflagは重複してカウントアップされることがあるので,resetUpdateflagではPDB()->updatecount -= card->updateflagという計数を行っている.⇒以下の手順で再現できる.ブロック選択して,1点削除→ 1点削除→ 一括削除→ Undo→ Undo→ Undo.バックアップイメージにupdateflagが残っているのではないだろうか?⇒どうも,そのようだ.UNDOSYSTEM::UndoRestoreの中でフラグをリセットすることで解消した.

通常,コマンド実行時やUNDO実行時にはupdatecount はリセットされるので,イメージに残っていると不都合が生じる.CARDTABLE:cleanUpdateflagは,①TOPOLOGY:UpdateRecordFuncと②FAMILYTREE::cancelUpdateの2箇所のみ.UpdateRecordFuncは更新レコードの送り出しを実行する.cancelUpdateは外部呼び出しのCancelUpdatelistで実行される.いずれも一覧表の更新に関わっている.ただし,後者は現在使われていないものと思われる.一応これでよいのではないかと思う.⇒CancelUpdatelistを廃止は廃止した.

▲結婚テーブルのlookupの操作を確認する.

PARTIALNAME::MakePartialListを廃止

PARTIALNAME::MakePartialListは現在使われていないようなので,廃止しておこう.さて,リンクテーブルとlookupの同期に関してかなり深刻な問題が発生している.部分図リストのレコード数がlookupで数えた場合とPDBで数えた場合で一致しないという現象だ.lookupがゼロ発進に対し,PDBは1発進になっているという違いがある.ただし,lookupのレコード番号と言うときは,1発進の数として扱っているはずだ.確かにどこかで躓きそうな感じはする.

まず,テーブルのサイズを確認しておこう.lookupはlongtableのインスタンスで,MAXPDBというサイズのlong配列を保持している.lookupはゼロ発進だから,これで問題ない.PDBはどうか?CARDTABLEはBASETABLEの派生クラスで,MAXPDBサイズのCARDLINK*配列だ.BASETABLEはARRAYという配列クラスの派生クラスで,ARRAYは指定したサイズのスロットを持つオブジェクトだ.

確かに,これはまずいような気がする.BASETABLEはtablesizeというメンバー変数を持っているが,ここには,オブジェクト生成時の配列サイズ,つまりMAXPDBが入っている.現行ではgetmaxrecnはtablesizeを返すようになっている.⇒まず,BASETABLE::tablesizeをprivateに指定して外部から隠蔽してみよう.その上でアクセス関数gettablesizeを追加して,tablesize-1を返すようにしてみる.念のため,tablesize→ tablesize2にリネームしてクラス内部での参照もチェックしておこう.⇒NODECLASS *newlinkから参照されていた.getmaxrecn,doClean, getnod, emptyrecn からもアクセスされている.

一般には,getmaxrecnまでのレコードは許容されているが,refnum >= gettablesize()でtablesize-1を返すとおかしくなるところも出てくる.たとえば,refnum >= gettablesize()をエラーとみなしているところなど…TableSizeOverなども同様の仕様になっている.その逆に,CARDTABLE::makelinkなどではrecn <= tablesizeならば適法としている.⇒まだ,見直しは完了していないが,ざっくり修正して通るかと思ったが,TOPOLOGY::MakeActiveListで起きているエラーはまだ止まらない.デバッグするしかないのだろうか?

2023/01/09のリストに戻ってみよう.登録レコード数:recordcount→ recordCount, 最大レコード番号: maxrecord→ getmaxrecn, _maxrecn としているが,テーブルサイズを押さえている関数は予定されていない.現行のgetmaxrecnはtablesizeを返しているので,実質それに該当しているのだが,maxrecordはテーブルサイズではなく,現に登録されている有効なレコード番号中の最大のものという位置付けだ.tableSizeでもよいような気はするが,最大レコードというところを強調したい.上の修正でgettablesizeに相当する関数だ.中を取って,getTablesizeとしてみよう.この関数はtablesize-1を返すことにする.現行のgettablesizeを置き換えるものだ.この関数を使っているところで,使われ方が正しいかどうかを点検してみよう.

getTablesizeはレコード番号の取れる最大値を意味している.⇒TableSizeOverという関数名は状態を示す関数としては適切ではないので,LinkTableFullに変更する.LinkTableFullはmaxrecordではなく,recordcountがtableSizeと等しくなることを意味する.⇒すでにそういう実装になっている.

カードの肩書に文字を書き込んで登録して,TOPOLOGY:MakeActiveListで(treeview->validcard != ActiveList->count)のエラーになる.TREEVIEWの側の値が間違っているものと思われる.TREEVIEW::validcardは「有効なカード数(基準カードと親族関係にあるカード数)」で多分これは画面上のカード数と一致するはずだ.この値は,①SetKinship,②validcardnum,③ExtractPartialCardのいずれかの方法で決定される.今の場合は③に該当する.⇒validcardnumはTOPOLOGYのメンバー変数,SetKinshipはTOPOLOGY:FilteringKinshipの中で,「親族範囲を確定する」ために実行される.

どうも,カードの登録更新で部分図ノードが増加してしまっているようだ.このサンプルには現在部分図が3つ登録されているが,そのうちの2番目の「3077直系血族図278点」がデフォルトになっている.編集対象のカードはこれら3つの部分図のすべてに登録されているので,部分図の登録に変化があるというのはおかしいのだが…

PartialListのカウントは278のままで変化していない.partialflagというのはCARDLINKの固有情報であり,Undo/Redoは実行されていないのだから,誤って上書きされることも考え辛い.partialflagにTRUEを設定している箇所は7箇所ある.ファイルオープン時には,InitLinkTable→ PartialLoad で既定の部分図が読み込まれる.

カードの登録でmakenewcardが掛かってくる.なぜだろう?アプリから送付されたCarddataのRefnumが空欄になっている!Personalには入っている.これはかなり由々しき事態だ.MyCardというのは,アプリに常設されているカード情報の格納域だ.MyCardはGetNewCardでロードされているはずだが…OpenFileProc→ GetNewCardでは入ってくる.mCARDDATA::Putで転記されていない!

どうもこの関数の中で例外が発生しているようだ.この関数では例外が発生することを想定していないので,voidになっている.例外をトラップしたところでGCERROR_CARDDATAPUTをスローするようにしたが,何も応答はない.この関数を呼び出しているmZelkova:mSendCardでキャッチして,GCERROR_SENDCARDで復帰している.SendCarDataにはエラー処理は入っているが,ごく一部のエラー以外は処理されていない.ERR_CARDTABLEOVERとERR_MARGTABLEOVERでは黙って復帰する.ERR_NOMEMORYの場合はアプリ終了する.

それ以外では戻り値の上位ビットを取って,その部分がSAMENAMEEXISTの場合には,黙って新規カードを作成する.これは「本人氏名と同じときは黙って新規カードを生成する」というルールによる.この仕掛けはあまり感心できない.SAMENAMEEXIST As Integer = &HFFFF0000なので,ほとんどのエラーコードと合致してしまう.NAMERECURRENCE As Integer = &HEFEF0000はなんとか識別できるのではないか?SAMENAMEEXISTのコードをFEFE0000に変えておこう.いや,これはすでに使われているので,F8F80000とする.

これで「本人氏名と同じ」とは衝突しないようになったが,どう処理すればよいか?当面,公式リリースはないと見込まれるから,エラーパネルを出して異常終了でよいのではないか?⇒異常終了ではなく,ノーマル終了とした.これで一応例外が発生していることだけは検出できるようになった.あまりユーザフレンドリでないパネルだが…

image

戻り値を持っている関数はエラーリターンできるが,void関数ではエラートラップに落ちたことはコンソールへのダンプでしか知ることができない.ほとんど見落とされてしまうので,void関数では例外をスローするようにしておこう.⇒16箇所のトラップにthrow文を入れた.

さて,問題は,mCARDDATA::Putでなぜ例外が発生しているのかという理由だ.理由ははっきりしていて,(carddata->Refnum != refnum)のときには例外をスローするという論理になっているためだ.「参照番号の改変」とされる事象だが,

いや,その前に別のエラーが発生してしまった.「本人の氏名データがありません」というエラーが起きている.

image

MakeNewCard→ Myself.Getで-5007というエラーが返っている.参照番号は3079.-5007というのはGCERROR_GETPERSONALだ.⇒修正ミスがあった.catchの{}の外にthrow文を置いてしまっていた.

例外をスローしているのは以下の一文だ.

if (carddata->Refnum != refnum) throw “mCARDDATA::Put 参照番号の改変”;

しかし,これはかなり奇妙だ.mZelkova::mSendCardでは,引数で渡されたmCARDDATA^ mycard… 以下のデータをDLL側に送付するものであり,CARDDATA card… 以下のオブジェクトはアプリ側のデータをDLL向けに変換するための受け皿として用意された関数内オブジェクトであり,まったく空の状態になっているはずだから,carddata->Refnum と refnumが一致するようなことは起こり得ないと考えられるのだが… 「参照番号の改変」という文字列はここにしか現れない.⇒この文をコメントアウトしてエラーなしで動作するようになった.

「改変」という文字列は,2019/08/13に一箇所出てくるだけだ.もちろん,「参照番号の改変」とはまったく別の文脈だ.サイトにアップロードされていない以前のログを探せば何か手がかりになる情報を見つけられる可能性はあるが,今のところ,全文検索ができていないので,それもできない.もしかすると,以前はCARDDATAを関数内ではなく,静的領域に置いていた可能性はあるかもしれない.そうなっていれば,一致することはあり得るように思われるが…

さて,どこまで進んでいたのか?わからなくなってしまった.エラーはすでに解消しているが,不良の原因となった,「新規カードの生成」で起きる不具合について,原因と対策を考えておかなくてはならない.部分図モードのときに新規生成されたカードは自動的にその部分図に属するようになるというのが仕様だったような気がするので,部分図のメンバーが増加すること自体は正当だが,あちこちで不整合が発生するというのは問題だ.どんな事象が起きていたのか?

カード情報を更新しようとして,新規カードが生成されたという事象だ.不良の原因ははっきりしている.lookupテーブルが更新されていないためだ.いまのところ,ファイルオープン時に一度生成されただけで,それ以外の処理は何一つ組み込まれていないのだから当然だ.カードの生成と削除に同期してlookupを時点処理するというのが現在の方針であり,その手続きを実装しなくてはならない.

lookupではレコード番号は必ずしも連続した並びにはなっていない.場合によっては,順序が逆転していることすらあり得るし,また,そのような場合でも正しく操作できなくてはならない.逆に言えば,もし,それが可能ならレコードの追加・削除ではテーブル全体の詰め合わせや並び替えをやらなくてもよいということになる.系統並び替えにはテーブルソートが入っていたはずだから,そのような処理はそこに任せてよいはずだ.従って,lookupの保守はかなり簡単なもので済むはずだ.

いや,maxrecordというのは仮に中間に空きがあったとしても,テーブル上では必ず末尾に来なくてはならないのではないか?⇒これは解釈の問題ではないかと思う.maxrecordが有効レコードの末尾であるということは,その(リンクテーブル上の)レコード番号の最大値であるということを意味する.定義としては,これで十分なのではないだろうか?カード追加・削除時のlookup更新手続きを書いてみよう.

  1. 新規カードが追加された場合には,そのレコード番号をlookupの末尾に追加し,lookup.countを+1する
  2. 既存カードの削除では,lookup上のそのカードのセルを空欄とする.

つまり,lookupのサイズは単調に増加するものとする.⇒しかし,どこかで整序する必要があるのではないか?これは結局,MakingLookUpを実行するということに他ならない.おそらく,CommandEndとUndoProcessの出口でやっておけば十分なのではないかと思われるが,もし,RebuildCardListでlookupを参照しているとすれば,その前に実行する必要があるかもしれない.⇒MakingLookUpを実行する必要はない.系統並び替え時にはつねにテーブル並び替えを実施している.テーブル並び替えとはlookupの整列に他ならない.

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

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

  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で同種のエラーが生じる.

TOPOLOGY::UpdateRecordはかなり危うい仕掛けだ

いよいよ大詰めと言ってよい.始業時バックアップを取ろうとしたら,下記のエラーが出た.⇒開発環境が立ち上がったままだった.

image

参照番号を永久コード化する,つまりマイナンバーコード化するということにした.これは決定であり,後戻りできない.いろいろなところに副次的な影響が出てくることだろう.それにしても,CardNumlist, MargNumlistには何を格納し,どのように使われているのだろう?基準ソートではリンクテーブルを操作しないということになったので,もしかするとまったく不要になっている可能性もある.というか,NAMESORTというオブジェクトは何のために設置されているのか?

NumlistはNAMESORT::BuildNumListで「テーブル並び替えの前後で参照番号のリストを生成」している.また,RestoreNumListでリンクテーブルに参照番号を書き戻している.それ以上の用途はないように思われるので,完全に止めてみることにしよう.特に,RestoreNumListでは参照番号を親番号として認識しているので,完全に法令違反だ.⇒明らかにNAMESORTというオブジェクトはCardNumListとMargNumListを管理するためだけに作られているので廃止できる.

いや,そういう訳にもゆかないようだ.①ソート基準列インデックス,②ソート順,③年号タイプ,④数字タイプなどのパラメータを保持している.NameSort,NameNarabekaeなどもこのクラスのメンバー関数だ.⇒CardNumListとMargNumListの完全廃止で問題なく動作している.⇒本線に戻ることにしよう.

maxrecnum→ recordcountのリネームはすでに完了している.maxrecn→ recordCountは未遂行だが,maxrecnをローカル変数として使っているところもあるので,そちらを先にリネームしておく必要がある._maxrecnはすでに使われているので,maxrecn_としておこう.⇒クラスメンバーとしてもmaxrecnはすでに存在していない.このmaxrecnはmaxRecordにリネームしているところからすると,関数と推定される.「maxrecordのアクセス関数をmaxRecordとする」となっているが,maxrecordは新設を予定されているものであり,まだ実装されていない.その前身として,maxrecn()が存在していたのだろうか?

maxRecord()はgetmaxrecn()を置き換えるべきものであり,「getmaxrecnではmaxrecordを返す」となっているのだから,getmaxrecn≒maxRecordと考えてよいはずだ.⇒「_maxrecnをリネームしてrecordCountとする」というのがある._maxrecn()という関数は存在し,recordcountを返しているから,recordcountを返す関数がrecordCountということになる.maxRecordという関数はまだ実装されていない.とりあえず,_maxrecn→ recordCountというリネームは実行できるはずだ.⇒対処した.

ともかく,「maxrecordを新設し,最大(リンク)レコード番号を格納する」というのをやっておこう.maxrecordへの参照を返すmaxRecord()も作っておく.maxrecordは「recordcountの更新時に更新」となっている.recordcountの更新関数には,以下がある.①incmaxrecn,②decmaxrecn,③cleartable,④MakingLookUp.しかし,これらの関数ではレコード番号自体の情報は与えられていないので,これらを呼び出しているところを見なくてはならない.

incmaxrecnはAMILYTREE::AppendFile,NODECLASS *newlinkから呼び出されている.decmaxrecnの呼び出しは,MARGTABLE:deletelink,CARDTABLE:deletelink,FAMILYTREE::AppendFileから掛かってくる.これらの位置で処理するより,incmaxrecn,decmaxrecnに引数を渡して,集中処理した方がよいと思う.「maxrecordは単調に増加するものとし,(基準ソート時以外では)テーブルは縮小されない」としているので,とりあえず,decmaxrecnでは最大レコード番号を更新しない.ただし,これは実装してもそれほどコスト増要因にはならないと考えられるので,実装した方がよい.

▲カード一括削除の後の基準ソートが入らなくなっているような気がする.もう一度クリックすればソートされる.これまでは動いていたような気がするのだが…

ともかく,ここで一度バックアップしておこう.⇒さて,問題はlookupとの関係だ.lookupとは,リンクテーブル上の有効レコードの参照番号を前詰めして格納したテーブルであると言える.lookupは常時更新されるのではなく,処理の最後に一括生成されるという仕様になっているから,recordcountやmaxrecordには関与しないと考えられる.⇒それでは,なぜMakingLookUpでrecordcountとlookup.countが一致しないのか?⇒UNDOではリンクテーブルは保全されていないので,オブジェクトの再生と削除は本来なら通常手続きとして実行されなくてはならないところだが,削除はdeleteで実行されているもの,再生は手続き的に実行されているため,recordcountが更新されていないものと見られる.

この処理はUNDOSYSTEM::RestoreShadowで実行される.この操作の対象となるオブジェクトはCARDLINKとMARGLLINKに限定されている.逆に言えば,それ以外のオブジェクト(の削除)はUNDO対象となっていない.⇒UNDOSYSTEM::RestoreShadowを修正して,incmaxrecnを実行するようにした.⇒これでつねにUNDO動作と関わりなくリンクテーブルを保守することができるようになった.

いや,まだ不一致が発生している.Redoした場合のデクリメントが入っていない.RestoreShadowはUndo/Redoの共通処理になっている.<削除>の場合はoperator deleteを実行しているだけなので,通常通りの処理になっていなくてはならないのだが… recordcountは307のままだ.なぜだろう?⇒CARDTABLE::deletelinkではdelete cardの後に,decmaxrecnを実行している.これは必須である.つまり,deleteを直接呼び出しただけではアプリ的な処理は実行されない.

BASETABLEの関数でnoderefnumというのがあるが,これはレコード番号を返す関数であり,この戻り値を参照番号とみなすべきではない.しかし,かなりの箇所でこの関数は使われている.⇒仕様を変更して,リンクから参照番号を返すように改めるべきだろう.⇒いや,この関数は仮想関数でクラス実装では参照番号を返すようになっているから,問題ないだろう.⇒BASETABLE::noderefnumは純粋仮想関数とした.また,BASETABLEにgetrecnの別バージョンを新設し,親枝番号をレコード番号として返すようにした.⇒大分整理されたのではないかと思う.

ここまでできると,lookupを常時更新することも可能になるのではないだろうか?lookupに何を格納しているのか?が問題だ.レコード番号を格納するというのが最適だが,現行では参照番号になっているのではないか?⇒いや,レコード番号になっている.これでよい.しかし,テーブル並び替えではリンクテーブルを直接ソートしている.これはあまりうれしくない.リンクテーブルは操作しないという原則に反する.しかし,これでも実際にはソートしたように見えているというのはなぜだろう?リンクテーブルが辞書順にソートされているとすると,テーブル並べ替えによってそれが崩れてしまうことになる.それによってアクセス不能になるという訳ではないので,とりあえず,問題なく表示できているのだろう.X⇒勘違い.下記参照

テーブル並び替えというのは一覧表の並びに関係するものであり,一覧表には部分テーブルを表示することもあるため,一覧表示されるカードについては,別途カードリストが編成されている.lookupというのは実際のところ,どの程度役に立っているのだろう?⇒MakeActiveListなどではlookupを直接参照している.⇒いや,読み違えしている.テーブル並び替えはlookup上で実施されている.

一つマズイのが出てきた.BASETABLE::getrefnumではlookupから引いた値を返している.これは参照番号ではなく,レコード番号だ.BASETABLE::getlinkがこの関数を使っている.⇒BASETABLE:getrefnum,getlinkは廃止した.⇒TOPOLOGYにもgetrefnumという関数がある.⇒アプリ側にはリンクテーブルのレコード番号はまったく公開されていないと考えるべきではないか?

この意味ではCallGetRecordNumのような外部関数には問題がある.逆に言うと,一覧表のレコード番号を常時当てにすることはできないから,外部からカードにアクセスするには,参照番号を使うしかないということにもなる.そうなると,lookupを使うメリットも薄れてしまうような気もする.参照番号は一応辞書順になっているので,それほど悪い設計とも言えないが…

アプリから参照番号でアクセスするとき,lookupを経由すると総当りになってしまう可能性があり,その意味ではむしろリンクテーブルにアクセスした方が効率はよいと考えられる.⇒アプリからレコード番号でアクセスするおそらく唯一の関数がZ.mGetRecordNumだが,この関数はレコード番号1のカードを取り出すか,テーブル全体にアクセスする場合にのみ使われているので,lookupで応答するように改造してみよう.

lookupはゼロ発進だったような気がするが…いや,1発進でよいようだが,だとすれば一つ大きい箱を用意しなくてはならないような気がするが… ⇒テーブルサイズはMAXPDBだ.つまり,MAXまで使うとオーバーフローしてしまう.いや,間違っている.やはりゼロ発進だ.⇒CallGetRecordNumをlookup対応に修正した.動作確認するには,mGetRecordNumの呼び出しをチェックしなくてはならない.

InitializeDisplay→ SendDateAll→ mGetRecordNumというフローがあるのだが… ⇒InitializeDisplayはファイルオープン時に一度呼び出されているだけだが,動作している.⇒アプリからレコード番号でアクセスする関数を総点検する必要がある.⇒一度バックアップを取ってから…

GetRecord(long num, char *rstr, int range)はおそらくレコード番号だと思う.GetRecordはrangeにより3つの処理に分岐する.①GetAllRecord,②GetActiveRecord,③GetPartialRecordだ.GetAllRecordはGetCardBaseRecordを呼び出している.この関数はlookup対応だ.しかし,GetActiveRecordから呼び出されるTOPOLOGY::GetRecordはリンクテーブルを対象としている.⇒この関数はlookup対応に書き直す必要がある.GetPartialRecordはPARTIALNAME::GetPartialRecordを呼び出している.ここではPartialListのレコード番号が使われているのでこのままとする.

GetActiveRecordというのは,一覧画面の表示範囲:系図画面上のカードを表示というモードだ.いや,参照しているテーブルは引数で渡されたもので,今の場合はActiveListを指しているので,間違ってはいない.参照番号からレコード番号への変換はgetrecnが使われているが,デフォルトの位置でマッチングしない場合は,リンクテーブル全体のブラインドサーチになっている.これはlookupを使った方が効率的なのではないか?また,レコードは通常デフォルト位置より上に配置される(参照番号の昇順)ことになるので,先に上方を検索し,見つからない場合のみボトムから探すとした方がよいのではないか?

SendUpdateDataもそうかもしれない.この関数はTREEVIEW:sendUpdateDataを呼び出しているが,レコード番号は参照されていない.⇒紛らわしいので使っていない引数は廃止することにしよう.SendUpdateData(long recn, int range, int len, char item[][MAXTEXTFIELD])の引数のうち,recnとrangeは使われていない.

ImportTableFuncは一覧表のレコード番号が使われている.⇒ブロック内変数に変更した.AutoMergeTest(long recn, int type)というのもある.AutoMergeTestはSingleMergeTestを呼び出している.⇒SingleMergeTestは「現在系図画面上に表示されているカードを対象にカード合併包括テストを実施する」ものなので,対象領域はActiveListである.現行でよいのではないかと思う.

COUPLING::getUpdateRecordでは戻り値でレコード番号を返している.実行関数はUpdateRecord.戻り値のレコード番号は対象領域により,lookup,ActiveList,PartialListとなっているので問題ないが,果たしてこのインタフェースで連続レコード処理できるのだろうか?mGetUpdateRecordを呼び出しているのは,UpdateRecordだけだ.UpdateRecordはUpdateTableのループの中で戻り値が0以下になるまで回している.引数はrangeだけなので,単なるトリガーだ.⇒mGetUpdateRecordは書き込みではなく,レコードの取り出し関数.

TOPOLOGY::UpdateRecordという関数はかなり特殊なことをやっている.これはじっくり解析しないと誤るかもしれない.この関数では内部状態を持っていて,updateindexをインデックスとして押さえている.対象領域は指定されたテーブルを対象としているのでよいようにも思われるが,かなり危うい仕掛けになっているような気がする.また,lookupに格納されているのはレコード番号であり,それ以外のテーブルは参照番号であるという点にも留意する必要がある.いずれにしてもかなり危ないような気がする.

参照番号のマイナンバーコード化

とりあえず,仕掛りとなっている recnとrefnumの切り分けというのが最大の課題だが,その前にいくつかペンディングを片付けておこう.

再現手順5でUndo→Undoしたとき,RestoreNumlistを実行すると,大量の「カード参照番号の重複」エラーが発生する.⇒この関数ではCardNumListとMargNumListに保全された参照番号をリンクテーブルに書き戻している.⇒手順5で最初に基準ソート→ Undoではエラーは発生しない.その後,一括削除→ 基準ソート→ Undo→ Undoで発生する.つまり,障害は基準ソートをUndoした時点ではなく,一括削除のUndoの時点で発生している.これは,一括削除時にNumListがバックアップされていないことを意味するのではないだろうか?

確かに,CardNumListとMargNumListをバックアップしているのは,基準ソートの場合だけだ.基準ソートをUndoした時点ではエラーは発生しない.Numlistが復元されているためだ.ただし,このリストは削除後のものであるため,短いものになっているので,テーブル上で更新されていない部分との間に矛盾が生じている.カード削除の場合などでもつねにバックアップを取るようにしておけばよいのかもしれないが(部分図リストなどはそうしている),時間コスト的にはかなり不利だ.

一番合理的なのは,参照番号をユーザから見えないようにしてしまうことではないだろうか?一覧画面の第一列にはつねに1から始まる通し番号を振っておけばよい.実用的にはこれで何ら差し支えないような気がする… つまり,参照番号はある種の「パーマネントコード」のようなものになる.コラッツ特注版では「氏名」を数値として解釈し,それに準ずるハッシュ値を生成しているが,将来的には「名前」でハッシングすることになると思う.さらに,データベース化ということまで考えると,やはり,参照番号の永久コード化というのは必須なのではないか?

もし,そうであるとすれば,RestoreNumlistという処理は完全に廃絶されることになるのではないだろうか?基準ソートという操作自体が廃絶されるとすれば,Renumberという機能も無用なものになると考えられる.⇒方向としてはこれが正しいのではないかと思われるのだが… その方向に一歩踏み出すことにしよう.Numlistを復元しないとすれば,保全する必要もなくなると考えられる.「参照番号を永久コード化する@20230109」で止めてみよう.

参照番号の隠蔽というのはよいとは思うが,「カード番号」というのは便宜上いずれにしても必要だ.一覧画面の通し番号でもよいが,一覧画面では一部カードのみを表示する場合があり,その番頭を採用するとなると,今度はカード画面との不整合が発生するようになる可能性がある.これを整合的に操作するのは意外に難しいと思う.むしろ割り切って,参照番号というのはマイナンバーカードのようなものとして公開してしまうという方が整合性が取り易いかもしれない.というか,それしかないような気もする.つまり,「参照番号のマイナンバー化」だ.

これが修正をミニマイズするもっとも賢明なやり方ではないか?これで昨日のチェックするとの項目1~3が片付いたので,いよいよリスト項目7のrecnとrefnumの切り分けに掛かることにしよう.これは前回途中までやって挫折したところだ.一度バックアップを取ってから始めよう.⇒ログを遡って読み直しておく必要がある.⇒この取り組みは2022-12-30頃から始まったものだ.要点をピックアップしておこう.

  1. 結婚リンクと親番号の不一致→ 暫定的に停止しないように措置した
  2. 人名テーブルと結婚テーブルについては,IsReferenceを使わない ⇒ この修正は撤回されたものと思われる
  3. maxrecnumをrecordcountにリネーム→有効レコードカウント
  4. recordcountは lookup テーブルの count に等しい
  5. maxrecordを新設し,最大(リンク)レコード番号を格納する
  6. recordcountの更新時に,maxrecordも更新する.
  7. maxrecordは単調に増加するものとし,(基準ソート時以外では)テーブルは縮小されない
  8. getmaxrecnではmaxrecordを返す
  9. maxrecordのアクセス関数をmaxRecordとする
  10. getmaxrecnを廃止して,maxRecordに統一する ⇒ getmaxrecnを廃止@20221226⇒_maxrecnにリネーム
  11. _maxrecnをリネームしてrecordCountとする
  12. CHECKREMAINSANSYOを一時停止 ⇒ 現在は有効
  13. longtableのaddを使う@20221226 ⇒ FIX
  14. setmaxrefnumでレコード番号を禁止@20221227 ⇒ FIX
  15. 並び替えではリンクテーブルは操作しない@20221228
  16. 構造的にオブジェクトの一部として完全に一体化している下位オブジェクトは,スロット接続ではなく,アンカー接続とするのが適当なのではないか?特に,参照リストは通常の参照管理とは別扱いとしなくてはならないのだから,その方が適切であるような気がする.
  17. @20221229で「枝番号と親番号が一致しない場合を許容する」という仕様変更 ⇒ この修正は破棄されたのではないか?
  18. ①UNDO処理,②参照管理のいずれか,ないし両方が未サポートの場合でも正しく動作しなくてはならない
  19. CheckMargLinkは静的情報とリンクの整合性をチェックしているので,CARDLINKのHUBOSからの参照が解除されているため,不一致が起きるのはやむを得ない ⇒ フラグを見て除外する⇒対処済み

まず,メンバー変数のリネームから始めよう.

  1. maxrecnumをrecordcountにリネーム→これはリンクテーブル上の有効レコードカウントを意味する.recordcountはlookup.countと一致しなくてはならない.recordcountはBASETABLE::cleartableで初期化され,incmaxrecnとdecmaxrecnで操作される.

  2. maxrecnをリネームしてrecordCountとする.
  3. maxrecordを新設し,最大リンクレコード番号を格納する.テーブルは穴空きの疎な状態で管理されるので,上限を押さえておく必要がある.
  4. recordcountの更新時に,maxrecordも更新する.
  5. maxrecordは単調に増加するものとし,(基準ソート時以外では)テーブルは縮小されない.
  6. getmaxrecnではmaxrecordを返す.
  7. maxrecordのアクセス関数をmaxRecordとする.

  8. getmaxrecnを廃止して,maxRecordに統一する

▲MakingLookUpでlookup->countをrecordcount に代入していたところを,一致しない場合には停止するようにしたところ,停止した.recordcount の 237に対し,lookup->countは307で70点の差がある.これはrecordcountのカウントに誤りがあるためと考えられる.⇒UndoProcess→ MakingLookUpでは不一致は避けられないのではないか?⇒一括削除のUndoなので,数字が一致しないのは当然のように思われる.recordcount はBASETABLEのメンバー変数だが,テーブルは基準ソート以外では保全されず,つねにMakingLookUpで再構築するようになっている.とりあえずは,現行論理を踏襲するしかない.

ここで二つのことが考えられる.①基準ソートではリンクテーブルは不変であるとすれば,リンクテーブルを保全する必要はないのではないか?カードの生成・削除はそれぞれのオブジェクトの操作で復元できるはずであり,リンクテーブルはUNDOから切り離すことが可能なのではないか?②UNDOがきちんと動作することが保証されるのであれば,PDB,MDBを保全する代わりにそれぞれのlookupを保全することが考えられる.リンクテーブルはテーブルの最大サイズ分のデータを保持しなくてはならないが,lookupなら有効データが前詰めされているので,かなり有利なのではないだろうか?

もし,これができればMakingLookUpでlookupを再構築する必要がなくなると考えられるのだが…⇒NAMESORTというオブジェクトがあり,CardNumlistとMargNumlistを管理しているが,これらは実質的にlookupと同内容なのではないだろうか?つまり,データとして重複しているのではないか?NAMESORTというのはFAMILYTREEの保持するオブジェクトだ.⇒とりあえず,UNDOでリンクテーブルを保全しない@20230109としてみたが,問題なさそうだ.