今回の記事は、運用時に偶然発覚した「%String型が%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 | 半分くらいが対象と誤認する |
4 | 4,5,6,7,8,9,10文字が対象と誤認する |
5 | 4,5,6,7,8,9,10文字が対象と誤認する |
6 | 4,5,6,7,8,9,10,11文字が対象と誤認する |
7 | 4,5,6,7,8,9,10,11文字が対象と誤認する |
8 | 4,5,6,10文字が対象と誤認する |
9 | 4,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型を誤認する文字列について理解を深めることで、予期しないエラーや意図しない動作を未然に防ぐことができます。
また、適切な検証と注意を払うことで、システム全体の安定性を高めることができと思います。
今回の記事は、記憶の片隅にでも留めておいていただけると幸いです。
最後までお読みいただき、ありがとうございました。