sql-serversqlcmdsql-server-2022context-info

Use of context_info returns different output in SQL Server Management Studio vs SQLCMD


I have this T-SQL code:

declare @DBNAAM varbinary(128) = cast((select DB_NAME() as [Current Database]) + '_SINGLES-' as varbinary(128));
set context_info @DBNAAM;
go

print cast(context_info() as varchar(128)) + 'XTRA_NAME'
go

If I run it in SSMS I get the output:

TEST_T24TSV_SINGLES-XTRA_NAME

That is what I expect, the dbname is TEST_T24TSV_SINGLES.

But if I run the same script with SQLCMD the output is:

T

So everything after the T disappears!

(If I copy the first output from SSMS to f.e. notepad, I also get only the T.)

(I want to use the construction with context_info, because I want to print a lot of this (after numerous GO's), and I want to have a one line statement for every print that I do. It is not possible to use a subquery as part of a print-statement.)

Why is SQL Server doing this? What can I change?


Solution

  • The problem is you're using the wrong data types.

    Firstly DB_NAME returns a sysname, which is a synonym for nvarchar(128) NOT NULL. You are casting it to a varbinary(128) (so could suffer truncation) and then casting that value to a varchar, so the second byte will now be seen as a null character.

    Secondly CONTEXT_INFO stores a binary value, not a varbinary, which means you get lots of null characters at the end of your string if you use a (n)varchar. Converting a binary to a varbinary doesn't remove the 0x00 bytes from the end of the value.

    So you need to convert to an nchar(128), then a binary(128) (as CONTEXT_INFO allows 128 bytes) and then SET the value; this means that the bytes at the end are whitespace not null characters. Then you can convert that value back to an nchar(128), RTRIM it (which implicitly turns it into an nvarchar) and then concatenate:

    DECLARE @DBNAAM binary(128) = CONVERT(binary(128),CONVERT(nchar(128),DB_NAME() +N'_SINGLES-'));
    SET context_info @DBNAAM;
    GO
    PRINT RTRIM(CONVERT(nchar(128),CONTEXT_INFO()))+N'XTRA_NAME';
    

    If you know that your database can only contain ASCII characters, then I suggest converting to a char(128) rather than an nchar(128) to avoid as much truncation as possible.

    db<>fiddle

    Damien_The_Unbeliever, however, does raise a good point about using SESSION_CONTEXT. This avoids the "hoops" and the value is stored using a sql_variant, so supports up to 8,000 bytes of data.

    A solution using this would look like this:

    DECLARE @DBNAAM sysname = DB_NAME() + N'_SINGLES-';
    EXEC sys.sp_set_session_context N'DBNAAM', @DBNAAM;
    GO
    
    PRINT CONVERT(nvarchar(128),SESSION_CONTEXT(N'DBNAAM')) + N'XTRA_NAME';