PerlのCPANモジュールの更新時に発生した問題の追記


YAPC::Hiroshima 2024がいよいよ来月に迫ってきましたね。

https://yapcjapan.org/2024hiroshima/

追加チケットはまだ間に合うようなので、Perlを触ったことがない人でもかなり楽しそうなトークが盛りだくさんっぽいのでよろしければぜひぜひ…

https://passmarket.yahoo.co.jp/event/show/detail/028iu3fs2fh31.html

---

と宣伝はこの辺にして、
その後今年に入って別の作業をしていて、昨年のCPANモジュールのバージョンアップをした際に発生した問題を3つほど思い出したので、個人的なメモとして置いておきます。

昨年12月の記事

https://qiita.com/teckl/items/060c1ce1c3ee7c42a2f6

の追記になります。

---

# Encode.pm で iso_2022_jp が使えなくなった問題

某所で古いメール送信処理が残っており、

```
Unknown encoding 'iso_2022_jp'
```

というエラーが発生していました。

```shell
$ export PERL_ENCODE_DEBUG=1
$ perl -MEncode -E 'say Encode::encode("iso_2022_jp", "hoge")'
delete $Alias{C} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{ascii} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{cyrillic} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{arabic} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{greek} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{hebrew} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{thai} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
eval "\L$1" at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 30.
find_alias(Encode, iso_2022_jp)->name =  at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 73.
Unknown encoding 'iso_2022_jp' at -e line 1.
```

これは `Encode` に対応するエイリアスが変わった問題のようで、
以下のように `iso_2022_jp` -> `iso-2022-jp` と変更することで動作するようになりました。

```shell
$ perl -MEncode -E 'say Encode::encode("iso-2022-jp", "hoge")'
delete $Alias{C} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{ascii} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{cyrillic} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{arabic} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{greek} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{hebrew} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
delete $Alias{thai} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
eval "\L$1" at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 30.
find_alias(Encode, iso-2022-jp)->name =  at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 73.
delete $Alias{macjapanese} at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 100.
iso-2022-jp => iso-2022-jp at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 61.
find_alias(Encode, iso-2022-jp)->name = iso-2022-jp at /usr/local/perl-5.36/lib/5.36.1/x86_64-linux/Encode/Alias.pm line 73.
hoge
```

今どきのメールはほとんど `UTF-8` になってきてるとは思いますが、そういう時代を感じるのもまた趣がありますねぇ。

---

# Calendar::Simple で開始曜日の仕様が変わっていた件

こちらは某所で使用していたカレンダーの実装で、気がつくと週の開始曜日が日曜から月曜に変更となっていました。
パッと見だとわかりにくいバグ（というか仕様変更）なので、最初なにが起きたのかと思いましたｗ

## 旧バージョン : 1.21

```
$ docker exec app perl -MCalendar::Simple  -E 'say $Calendar::Simple::VERSION'
1.21
$ docker exec app perl -MCalendar::Simple -MData::Dumper -E 'say Dumper( @{Calendar::Simple::calendar(8, 2023)}[0] )'
$VAR1 = [
          undef,
          undef,
          1,
          2,
          3,
          4,
          5
        ];
```

## 新バージョン : 2.0.1

```
$ docker compose exec app carton exec -- perl -MCalendar::Simple  -E 'say $Calendar::Simple::VERSION'
2.0.1
$ docker compose exec app carton exec -- perl -MCalendar::Simple -MData::Dumper -E 'say Dumper( @{Calendar::Simple::calendar(8, 2023)}[0] )'
$VAR1 = [
          undef,
          1,
          2,
          3,
          4,
          5,
          6
        ];
```

https://metacpan.org/dist/Calendar-Simple/changes

を見てみると、以下のような記述が。

> ## [2.0.0] 2020-06-22
>  
> ### Changed
>
> - Default week start date is now Monday

元々は日曜開始用のカレンダーでしたが、
新しい 2.0.0 から月曜開始というのがデフォルトになってしまっているようです。

コンストラクタに 0 を指定すると直るようなので、以下のようにして対応しました。

```
$ docker-compose  exec app carton exec -- perl -MCalendar::Simple -MData::Dumper -E 'say Dumper( @{Calendar::Simple::calendar(8, 2023, 0)}[0] )'
$VAR1 = [
          undef,
          undef,
          1,
          2,
          3,
          4,
          5
        ];
```

できれば互換性を残してほしかったですが、メジャーバージョンが変わるときに深い理由があったんでしょうかねぇ。

---

# DBI で取得したカラムの型が文字列から数値に変わった件
この問題が一番痺れたバグでした。

https://shogo82148.github.io/blog/2021/10/14/json-pp-on-perl/

ととても似た問題です。（内部的には繋がってるかも？）

DBIでDBからあるテーブルのカラム（カラム自体はint）を取得した際、これまで文字列だったものが、数値型に変わってしまっているというものでした。

## こんな感じ（簡略化しています）
```perl
my $dbh = DBI->connect($dsn, $user, $password);

my $sth = $dbh->prepare(
    'SELECT id, page FROM xxxxx WHERE id = ?')
    or die "prepare statement failed: $dbh->errstr()";
$sth->execute('57590') or die "execution failed: $dbh->errstr()";
while (my $ref = $sth->fetchrow_hashref()) {
    print "Found a row: id = $ref->{'id'}, fn = $ref->{'page'}\n";
    warn Dumper($ref);
}
$sth->finish;
```

### 旧環境の出力

```shell
Found a row: id = 57590, fn = 0
$VAR1 = {
          'page' => '0',
          'id' => '57590'
        };
```

### 新環境の出力

```shell
Found a row: id = 57590, fn = 0
$VAR1 = {
          'page' => 0,
          'id' => 57590
        };
```

Perl的には `0` でも `'0'` でもコンテキストでよしなにやってくれるので良いのですが、

```shell
$ perl -E "say '0' eq 0"
1
$ perl -E "say '0' == 0"
1
```

この値がJSON化(`JSON::to_json`)されて別の箇所で利用されており、
悪いことに某所でjs側で以下のようなよろしくないロジックが入ってしまっておりました。。

```javascript
if (mode === '0') {
  mode = 1234;
}
```

JavaScriptだと文字列の `'0'` と数値の `0` では `===` だと全然違う挙動になってしまうので、
非常に微妙なバグになってしまっていました。
幸いテスト期間で発見できたのでユーザ影響はなかったのですが、

https://shogo82148.github.io/blog/2021/10/14/json-pp-on-perl/

の記事にもあるように、

https://metacpan.org/pod/JSON::Types

https://metacpan.org/pod/Cpanel::JSON::XS::Type

を使って型を明示的にしたほうが良さそうですねぇ。。

本件ですが、当時DBIの実装なども少し見てみたのですが、具体的にどこのアップグレードの時点で発生したのかを追いきれませんでした。
`DBI::st->bind_col` のXSモジュール側あたりの問題でしょうか…？
有識者の方がいらっしゃいましたらご教示いただけると助かります。。
この現象が一番恐ろしかったやつとして思い出したので、ここにメモとして残しておきます。

8年ぶりにバージョンを上げようとするとこういう痛い事になったりするので、
何事も溜め込まない方が良いですねぇ、という感じでした。

【2024-01-14 11:35追記】
本件、有識者から教えていただきました！

https://twitter.com/AnaTofuZ/status/1746319715461652800

どうやら、

https://github.com/perl5-dbi/DBD-mysql/pull/57

https://metacpan.org/dist/DBD-mysql/changes#L263

の `DBD::mysql` の4.038でこの変更が入っていたようです。

> Perl's IV in scalar can store 64bit integer when perl was compiled with
> 64bit support (default on 64bit linux with gcc). Use this feature and
> stores MYSQL_TYPE_LONGLONG as integers instead strings when possible.

確かにMySQL 8.0対応に伴って `DBD::mysql` は `4.033` -> `4.050` に上げていたのでした。。

原因がわかってスッキリしました。 アナグラさん、ありがとうございました！

---

以上になります。
もし他にも思い出したら追記などできればと思います。