【IRIS/Cache】%List型を誤認する!(大惨事発生)

今回の記事は、運用時に偶然発覚した「%String型が%List型と誤認する」ケースのご紹介です。

※この記事は下記の方向けになります。
  • %List型と%String型を混在して運用している方
  • $listValid()を使用して、%List型を判定している方

はじめに

ご存じの通り%List型は文字列の一種で、特定のパターンの文字を組み合わせて実現しています。

これはこれですごい技術ですが、上記仕様の為、特定のパターンの文字を組み合わせると、当然人為的に%List型を再現する事が可能です。

ただし、InterSystems社では%List型の内部構造(判定方法)を公表していないため、そのパターンを正確に理解する事はできません。

正直、気にしたことも無かったです。(笑)

・・・と、思っていた時期がありましたが、運用時の事故から仕様の一部を知ることになりました。
今回は、偶然知ってしまった仕様の一部をご紹介致します。

簡単な仕様の確認

先ずは%List型について、簡単な仕様の確認をしたいと思います。

s list = $lb("a","b")
w $l(list) // → 6と表示される

「a」「b」の2文字を格納しましたが、文字数は6と出ています。
これは文字列を%List型として成立させるため、「a,b」以外に制御文字を文字列に加えているからです。

では、どのような組み合わせなのか確認しましょう。
1文字づつ切り取って、文字を数値コードに変換して確認します。

f pos=1:1:$l(list) { w $a($e(list,pos)),! } w "end"

【関数の実行結果】
3
1
97 // $c(97) > a
3
1
98 // $c(98) > b
end

実行すると、「$c(3) + $c(1) + a」と「$c(3) + $c(1) + b」の組み合わせになっている事が確認できます。

このように%List型は、制御文字を特定のパターンで組み合わせる事で、運用している事が認識できました。

判明の経緯

それはユーザの入力した値を、共通関数を通して画面へ返す箇所で発生しました・・・

共通関数内で%List型%String型かを判定していましたが、通常の文字列を%List型と誤認して画面側へ返していたため、画面側がエラーとして感知し業務が一時停止する事になりました。

この事象が発生したタイミングが、cache2013からcache2018にバージョンアップした翌日だったので、バージョンアップ時の調査不足か!と背筋に冷たいものが走ったものです。
 ※結局はリリースノートに記載がなく、内部仕様の変更と結論付きました。

この事象に関しては、Cache2015以降より発生します。

その時に判明した仕様が下記になります。
 ※実際に発生した文字列とは異なります。

s str="" f pos=1:1:46 s str=str_"1" // 111111...と1を46個連結
s val="1"_$c(13,10)_str // 改行コードを含め、全体で49文字にする
w $lv(val) // → 1と表示される

解説

事故発生時に判明した%List型の仕様は、下記3つの条件を満たす事にあります。

  • 3文字以上の文字列である事
  • 文字列の2番目にASCIIコード「CR ($c(13))」が含まれている事
  • 1番目の文字の「1」のバイト数は0x31(16進数)、10進数だと49で文字数と一致する事

上記条件を踏まえると、下記文字列でも%List型として認識するのが理解できます。
 →1番目の文字が$char(3)、2番目の文字が$char(13)、文字数3文字のため条件が一致

s val=$c(3,13)_"1"
w $lv(val) // → 1と表示される

これ、本記事冒頭で確認した文字列と似ていますよね。

サンプルは全てIRIS 2023.1.2.450.0で確認しています。

他に条件がないか確認する

基本的な条件が判明したので、他に条件がないか確認してみます。
 →$char(1)で発生する事は、本記事冒頭で確認済み

3文字以上で組み合わせを確認する

チェック用の処理を作成したので、ターミナルに張り付けて実行してみます。

Sample(max,char,string)	;
	k data
	f len=3:1:max {
		s str = ""
		f pos=1:1:(len-2) s str=str_string
		s test = $c(len)_$c(char)_str
		s:$lv(test) data(len)=""
	}
	
	// データチェック用
	i ($d(data)){
		w !,"-----"
		zw data
		w !,"-----"
		f pos=3:1:max {
			w:'$d(data(pos)) !,pos
		}
	}else{
		w !,"対象なし"
	}

下記コマンドを実行し、3~255文字数までの中で、どの条件が%List型と誤認するかまとめました

d Sample(255, [ASCIIコード値], "あ")

誤認する対象となるのが、下記になります。

ASCIIコード(10進数)3~255までの文字数
1全ての文字数で%Listと誤認する
2半分くらいが対象と誤認する
44,5,6,7,8,9,10文字が対象と誤認する
54,5,6,7,8,9,10文字が対象と誤認する
64,5,6,7,8,9,10,11文字が対象と誤認する
74,5,6,7,8,9,10,11文字が対象と誤認する
84,5,6,10文字が対象と誤認する
94,5,6,7,8,9,10文字が対象と誤認する
12全ての文字数で%Listと誤認する
13全ての文字数で%Listと誤認する

この中で見ると、やはりキーボードで入力できる値である「$char(13)」が問題を起こしそうな雰囲気を醸しています。
 ※タブ[$c(9)]は、誤認する文字数の範囲が狭いため、事故が発生する確率は低い

また、$char(14)以降の文字は、%List型と誤認する事はなさそうです。

その他での誤認ケース

誤認するパターンが他に存在しないか確認してみます。

1文字

下記サンプルより、$char(1)のみが対象と確認できます。

Sample()	;
	f pos=1:1:1000 {
		w:($lv($c(pos))) pos,!
	}
d Sample() // → 1と表示される

w $lv($c(1)) // → 1と表示される

2文字

2文字で確認を行います。
$char(1,2,4,5,8,9,12)で%List型と誤認しました。

w $lv($c(2,0))
w $lv($c(2,1)) // → 1 と表示される
w $lv($c(2,2)) // → 1 と表示される
w $lv($c(2,3))
w $lv($c(2,4)) // → 1 と表示される
w $lv($c(2,5)) // → 1 と表示される
w $lv($c(2,6))
w $lv($c(2,7))
w $lv($c(2,8)) // → 1 と表示される
w $lv($c(2,9)) // → 1 と表示される
w $lv($c(2,10))
w $lv($c(2,11))
w $lv($c(2,12)) // → 1 と表示される
w $lv($c(2,13))

おわりに

いかがだったでしょうか。

他にも色々と条件があるかもしれませんが、際限が無いため今回はここまでにしたいと思います。

%List型を誤認する文字列について理解を深めることで、予期しないエラーや意図しない動作を未然に防ぐことができます。
また、適切な検証と注意を払うことで、システム全体の安定性を高めることができと思います。

今回の記事は、記憶の片隅にでも留めておいていただけると幸いです。

最後までお読みいただき、ありがとうございました。