某套老系統在新裝的測試環境噴出 SQL String or binary data would be truncated. (字串或二進位資料會被截斷) 錯誤。

詭異之處在於:引發錯誤的輸入內容很簡短,想不出有超出 VARCHAR 長度的可能,加上該系統運作了十多年,第一次在這個環節遇到字串過長被截錯誤。

百思不得其解,搬出 SQL Profiler 側錄 SQL 指令,找到兇手:

EXECUTE sp_executesql N'INSERT INTO MyTable ....', /** 略 **/
@IPTrace=N'192.168.10.123/fe80::6d34:4e21:bf55:7451%4',
/** 略 **/

靠,居然是踩到 IPv6 的雷! 這台新主機在安裝時忘記停用 IPv6 協定,因此導致問題。

資料表當初保留一個欄位記錄客戶端與伺服器 IP,方便事後追查。原本盤算 IP XXX.XXX.XXX.XXX 最多 15 字元,兩個 IP 位址加上 "/" 分隔符號最多 31 個字元,欄位設成 VARCHAR(32) 綽綽有餘。客戶端 IP 來自 Request.UserHostAddress 沒問題,但伺服器本機 IP 抓成 IPv6 位址讓字串長度破表。進一步確認,老系統用了一顆舊元件,其使用 System.Net.Dns.GetHostEntry(Dns.GetHostName()).AddressList[0].ToString(); 寫法取得伺服器 IP,這種做法已知在 Windows 2008/Windows 7 以後的系統會遇到 IPv6 問題,即便停用 IPv6 抓到的也是 ::1 而非 IPv4 地址,正確解法是加上過濾條件寫成 Dns.GetHostEntry(Dns.GetHostName()).AddressList.First(o => o.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).ToString();。找出問題根源,換版共用元件後故障排除。

An old web application throwed weird SQL "string or binary data would be truncated" error on new enviroment. It was found that Dns.GetHostEntry() get IPv6 address by mistake and exceeded VARCHAR column length. The bug was fixed after AddressFamily.InterNetowrk filter added.


Comments

# by Greg Yu

「該系統運作了十多年」 這樣的老系統還要搬到新的機器上, 似乎表示還打算再戰下個10年, 到時候會不會真的得改用 IPv6 呢? 我想,那真的是後人才需要擔心的問題了...

# by Jeffrey

to Greg Yu, 這是個有趣問題,我剛好也在想:在 LAN 環境多半會用 172.*、192.168.* 等內部 IP,是否有必要轉用 IPv6?會不會只是徒增複雜度?

# by 孫守真任真甫

靠,果然很靠譜。呵呵 感恩感恩 讚歎讚歎 南無阿彌陀佛

# by Yang

為何不考慮放寬欄位長度呢?

# by Jeffrey

by Yang, 工作團隊平日溝通仍以 IPv4 地址為準,放寬欄位改存 IPv6 反而造成困擾。

Post a comment