What is the proper type for the rowversion (timestamp) data type?
I know it is 8 bytes but i cannot find a link in MSDN which tell if it is a signed or unsigned long.
which code should I use, does it even matter?
byte[] SqlTimeStamp;
long longConversion;
longConversion = BitConverter.ToInt64(SqlTimeStamp,0);
TimeStamp = BitConverter.GetBytes(longConversion);
ulong ulongConversion;
ulongConversion = BitConverter.ToUInt64(SqlTimeStamp,0);
TimeStamp = BitConverter.GetBytes(ulongConversion);
Short answer: it doesn't matter but I'd choose UInt64
.
Details: semantically it's equivalent to binary(8)
so, strictly speaking, it's neither UInt64
nor Int64
but just a chunk of bytes (and in that way it should be managed). That said I'd choose UInt64
because it's an incrementing number to hold row version then (from a logic point of view) 0xFFFFFFFFFFFFFFFF
should be greater than 0
and it's not true for Int64
(because 64 bits set to 1 give -1
and it's less than 0
).
Edit: note that, for reasons known only in the innest SQL Server designers circle, ROWVERSION
is big-endian (while - obviously - bigint
is not) then you first need to reverse bytes, see this answer for a nice implementation.
It does matter. You want your comparison to have the same result as SQL Server's comparison. SQL Server uses unsigned comparisons on binary types:
select case when 0x0FFFFFFFFFFFFFFF < 0xFFFFFFFFFFFFFFFF then 'unsigned' else 'signed' end
If you do the same thing with long
which is signed, 0xFFFFFFFFFFFFFFFF
represents -1
. That means your comparison will be incorrect; it won't match with the same comparison done in SQL Server.
What you definitely want is to use ulong
where 0xFFFFFFFFFFFFFFFF
is ulong.MaxValue
.
Endianness is also important
Additionally, as Mark pointed out, BitConverter.GetUInt64
is not converting properly. Mark is not completely right- BitConverter
is either big-endian or little-endian depending on the system it's running on. You can see this for yourself. Also, even if BitConverter was always little-endian, Array.Reverse
is less performant with a heap allocation and byte-by-byte copying. BitConverter
is just not semantically or practically the right tool for the job.
This is what you want:
static ulong BigEndianToUInt64(byte[] bigEndianBinary)
{
return ((ulong)bigEndianBinary[0] << 56) |
((ulong)bigEndianBinary[1] << 48) |
((ulong)bigEndianBinary[2] << 40) |
((ulong)bigEndianBinary[3] << 32) |
((ulong)bigEndianBinary[4] << 24) |
((ulong)bigEndianBinary[5] << 16) |
((ulong)bigEndianBinary[6] << 8) |
bigEndianBinary[7];
}
The cleanest solution
Here is the solution I use: Timestamp.cs.
Basically once you cast to Timestamp
, you can't go wrong.
Neither will work correctly for purposes of comparing timestamp/rowversion values, if you're running on an x86 family CPU, because of endian. The first byte of a timestamp is most significant, but not so for little endian integer types.
Call Array.Reverse(ts) before calling BitConverter.ToUInt64(ts), and for the other direction, after calling BitConverter.GetBytes(tsUInt64)
I use this:
private UInt64 GetUInt64ForRowVersion(byte[] rowVersion)
{
if (BitConverter.IsLittleEndian) { Array.Reverse(rowVersion); }
return BitConverter.ToUInt64(rowVersion, 0);
}