Linux

為什麼 sqlcmd 會截斷 varbinary(max)?

  • December 18, 2017

sys.assembly_files目錄內部,我可以看到一個名為content

1> SELECT assembly_id, LEFT(name, 50), file_id, content FROM sys.assembly_files;
2> GO
assembly_id                                                    file_id     content
----------- -------------------------------------------------- ----------- -------
         1 microsoft.sqlserver.types.dll                                1 0x4D5A90x07585

(1 rows affected)

然而,即使sqlcmd -Y 8000 -y 5000我不能得到更多0x4D5A90x075850x167D00x043CB

assembly_id                                                    file_id     content
----------- -------------------------------------------------- ----------- -------
         1 microsoft.sqlserver.types.dll                                1 0x4D5A90x075850x167D00x043CB

想知道發生了什麼並懷疑客戶端沒有將所有內容都返回給我,我嘗試了SQL Servermssql-cli的埠。pg-cli使用mssql-cli,我看到了整個內容欄位,實際上它看起來確實是位內容的正確大小。這讓我相信這是sqlcmd.exe截斷或其他破壞和摻假程序集的字節碼表示的問題。使用mssql-cli,我明白了

master>SELECT assembly_id, LEFT(name, 50), file_id, content FROM sys.assembly_files;
...... 
-[ RECORD 1 ]-------------------------
assembly_id      | 1
(No column name) | microsoft.sqlserver.types.dll
file_id          | 1
content          | 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000D00000000E1FBA0E00B409CD21B........ and on and on
(1 row affected)

為什麼 sqlcmd 會截斷 varbinary(max)?

因為它可能是 GB 的數據,所以 SQLCMD 和 SSMS 等查詢工具會截斷結果以顯示。

您可以使用 bcp.exe 轉儲到文件,將“前綴長度”設置為 0。

bcp "select content from sys.assembly_files where name = 'microsoft.sqlserver.types.dll'" queryout microsoft.sqlserver.types.dll -T

Enter the file storage type of field content [varbinary(max)]:
Enter prefix-length of field content [8]: 0
Enter length of field content [0]:
Enter field terminator [none]:

Do you want to save this format information in a file? [Y/n] n

Starting copy...

1 rows copied.
Network packet size (bytes): 4096
Clock Time (ms.) Total     : 62     Average : (16.13 rows per sec.)

您看到的不是截斷;它一定是一個錯誤。既不0x4D5A90x07585是也不0x4D5A90x075850x167D00x043CB是有效的二進制值。應該只出現0x一次(最多),在最左邊,並表示後面的值是二進制值。對於二進制值來說,再顯示一次或兩次是不可能的,因為在十六進制/以 16 為基數的符號中不能有“x”。截斷將是值的前 N ​​個字節,而您看到的不是。如果執行以下操作:

SELECT CHARINDEX(N'07585', CONVERT(NVARCHAR(MAX), [content], 1))
FROM sys.assembly_files;

您應該返回值 8003,表明輸出中的第二組 5 位十六進制數字不是該程序集中的第二組 5 位數字。而且,當實際字節需要 2 個十六進制數字時,每個“片段”(以 開頭0x)只有 5 個十六進制數字,因此它們應該始終以 2 個為一組,這也很奇怪。

SQLCMD(至少在 Windows 上)似乎有 524,287 字節的限制,這足以輸出您正在使用的 392,376 字節程序集的全部內容(即microsoft.sqlserver.types.dll)。

使用 SQLCMD,您可以使用該-y開關設置可變長度欄位的最大大小,即該[content]欄位。您可以設置的最大值-y8000,但如果您使用0的值,那麼它將達到 524,287 字節。

嘗試以下操作,看看你會得到什麼:

sqlcmd -S server auth_stuff -y 0 -Q "SELECT [content] FROM sys.assembly_files;" -o .\binary_truncation.txt

該查詢假定您沒有載入任何自定義程序集。如果這樣做,則可以通過在該查詢的末尾添加以下內容來縮小特定範圍:

WHERE assembly_id = 1 AND [file_id] = 1

每個大會的[file_id] = 1行是大會本身。附加文件(如果有)將從[file_id] = 2.

當我在 Windows 上執行該命令行時,我得到了一個 784,777 字節的文件。去掉幾個換行符的 25 個字節,“(受影響的 1 行)”行和開頭的“0x”,剩下 784,752 個字節。二進制的每個字節佔用 2 個單字節字元,00 - FF,因此將該文件大小(減去 25 個字節)除以 2 得到 392,376 個字節。執行以下操作:

SELECT LEN([content]) FROM sys.assembly_files;

回報:392,376。因此,我們提取了整個程序集。

現在,如果確實存在僅VARBINARY輸出的錯誤,我們可以先NVARCHAR通過執行以下命令將其轉換為數據來進行測試:

sqlcmd -S server auth_stuff -y 0 -Q "SELECT CONVERT(NVARCHAR(MAX), [content], 1) FROM sys.assembly_files;" -o .\binary_truncation.txt

對我來說,這會返回相同的 784,777 字節文件。

引用自:https://dba.stackexchange.com/questions/193329