2

I'm trying to convert back the nvarchar stored in CONTEXT_INFO.

declare @LanguageCode nvarchar(6) = 'en';
declare @binvar varbinary(128);
set @binvar = cast(@LanguageCode as varbinary);
set context_info @binvar;

select A = len(@LanguageCode);
select B = len(convert(nvarchar(6),@LanguageCode));

select C = convert(nvarchar(6),context_info());
select D = cast(CONTEXT_INFO() as nvarchar(5));
select E = len(convert(nvarchar(6),CONTEXT_INFO()));
select F =len(cast(CONTEXT_INFO() as nvarchar(6)));

select G = convert(nvarchar,context_info());
select H = cast(CONTEXT_INFO() as nvarchar);
select I = len(convert(nvarchar,CONTEXT_INFO()));
select J = len(cast(CONTEXT_INFO() as nvarchar));

The nvarchar obtained from casting varbinary to nvarchar is of length 6, it includes the \0. Example, the language 'en' is obtained back as 'en\0\0\0\0'. This mucked up the key caching on NHibernate.

Is there a way we can convert back the nvarchar value stored in varbinary to its original value? i.e., 'en' can be converted back to 'en'.

Output:

A
-----------
2

B
-----------
2

C
------
en    

D
-----
en   

E
-----------
6

F
-----------
6

G
------------------------------
en                            

H
------------------------------
en                            

I
-----------
30

J
-----------
30

CHOSEN ANSWER rationale

I chose HABO's answer as his solution is directly embeddable to consuming queries:

declare @LanguageCode nvarchar(6) = 'en';
declare @binvar varbinary(128);
set @binvar = cast(datalength(@LanguageCode) as varbinary) + cast(@LanguageCode as varbinary);
set context_info @binvar;
select dump = @binvar;



-- directly embeddable
select  recoveredLanguage = convert(nvarchar, substring(context_info(), 5, convert(int, substring(context_info(), 1, 4)) )  ) 
        , theLength =  len ( convert(nvarchar, substring(context_info(), 5, convert(int, substring(context_info(), 1, 4)) )  ) )


declare @buffer varbinary(128) = context_info();
declare @RecoveredLanguageCode nvarchar(6) = cast(substring(@buffer, 5, cast(substring(@buffer, 1, 4) as int)) as nvarchar(6));
select recoveredLanguage = @RecoveredLanguageCode, theLength = len(@RecoveredLanguageCode);

Output:

dump
--------------------
0x0000000465006E00

(1 row(s) affected)

recoveredLanguage              theLength
------------------------------ -----------
en                             2

(1 row(s) affected)

recoveredLanguage theLength
----------------- -----------
en                2

(1 row(s) affected)

While souplex's answer is correct, I'm not certain if SET ANSI_PADDING_OFF effects globally, or local only on one batch of statements. And placing all the castings in one statement has side effect, the restoration doesn't work, each casting must be done on separate statements. I upvote souplex's answer anyway

DECLARE @LanguageCode NVARCHAR(6) = 'en';
DECLARE @binvar VARBINARY(128);
SET @binvar = CAST(@LanguageCode AS VARBINARY(128));
SET context_info @binvar;
select dump = @Binvar;




SET ANSI_PADDING OFF;
select recoveredLanguage = cast( cast( cast(context_info() as binary(128)) as varbinary(128) ) as nvarchar(6) )
        , theLength = len( cast( cast( cast(context_info() as binary(128)) as varbinary(128) ) as nvarchar(6) ) );


DECLARE @binvar1 BINARY(128) = context_info();
DECLARE @binvar2 VARBINARY(128) = CAST(@binvar1 AS VARBINARY(128));
declare @c nvarchar(6) = cast(@binvar2 as nvarchar(6))
select recoveredLanguage = @c, theLength = len(@c);

Output:

dump
----------
0x65006E00

(1 row(s) affected)

recoveredLanguage theLength
----------------- -----------
en                6

(1 row(s) affected)

recoveredLanguage theLength
----------------- -----------
en                2

(1 row(s) affected)

Kudos to HABO and souplex for not bringing up XML PATH or XML AUTO solution :-) Thanks!

Community
  • 1
  • 1
Hao
  • 8,047
  • 18
  • 63
  • 92
  • why don't you 'declare @binvar varbinary(4);'? just enough to hold the characters and you don't have trailing \0\0\0\0 – Khanh TO Jun 16 '13 at 02:30
  • @KhanhTO some language codes are not fixed length. e.g., en-US, zh-CN, zh-TW, zh-CHS, zh-CHT – Hao Jun 16 '13 at 02:43

2 Answers2

3

Prefix the string with the length before you save it in CONTEXT_INFO:

declare @LanguageCode nvarchar(6) = 'en';
declare @binvar varbinary(128);
set @binvar = cast(datalength(@LanguageCode) as varbinary) + cast(@LanguageCode as varbinary);
set context_info @binvar;

declare @buffer varbinary(128) = context_info();
declare @RecoveredLanguageCode nvarchar(6) = cast(substring(@buffer, 5, cast(substring(@buffer, 1, 4) as int)) as nvarchar(6));
select @RecoveredLanguageCode, len(@RecoveredLanguageCode);
HABO
  • 15,314
  • 5
  • 39
  • 57
  • Or just truncate at the first `char(0)` – ErikE Jun 16 '13 at 03:08
  • @ErikE - My preference would be to keep the information I started with rather than adopt a `c` attitude of `\0` must be the end. Testing with an initial string of `'en'+char(0)+'x'` resulted in the string being recreated correctly. Both the initial and recreated string display as `en`, however casting them to `varbinary(128)` shows that it is merely a display artifact caused by SSMS stopping at the `\0`. – HABO Jun 16 '13 at 03:30
2

A regular varbinary(128) would be perfectly castable.

DECLARE @LanguageCode NVARCHAR(6) = 'en';
DECLARE @binvar VARBINARY(128);
SET @binvar = CAST(@LanguageCode AS VARBINARY);
SELECT  CAST(@binvar AS NVARCHAR(6));

This returns the initial value 'en' back.

Now when you set the context value and retrieve it back, it seems to have turned into a binary(128) value rather then a varbinary(128).

SET context_info @binvar;
SELECT  CAST(context_info() AS NVARCHAR(6));

Now you have trailing char(0) characters.

When you cast the context_info() value to a varbinary and cast that to a nvarchar your issue is solved when you explicitly set ansi padding to off.

SET ANSI_PADDING OFF;
--
DECLARE @LanguageCode NVARCHAR(6) = 'en';
DECLARE @binvar VARBINARY(128);
SET @binvar = CAST(@LanguageCode AS VARBINARY(128));
SET context_info @binvar;

SET ANSI_PADDING ON;
DECLARE @binvar1 BINARY(128) = context_info();
DECLARE @binvar2 VARBINARY(128) = CAST(@binvar1 AS VARBINARY(128));
--> having trailing char(0)
SELECT  CAST(@binvar2 AS NVARCHAR(6));

GO

DECLARE @LanguageCode NVARCHAR(6) = 'en';
DECLARE @binvar VARBINARY(128);
SET @binvar = CAST(@LanguageCode AS VARBINARY(128));
SET context_info @binvar;

SET ANSI_PADDING OFF;
DECLARE @binvar1 BINARY(128) = context_info();
DECLARE @binvar2 VARBINARY(128) = CAST(@binvar1 AS VARBINARY(128));
--> no more trailing char(0)
SELECT  CAST(@binvar2 AS NVARCHAR(6));
souplex
  • 981
  • 6
  • 16