From fbdc95ede662f0859790d416108f31fb7d1494a2 Mon Sep 17 00:00:00 2001 From: Dimitri Fontaine Date: Mon, 18 Feb 2019 19:37:42 +0100 Subject: [PATCH] Fix parsing of MS GUID with mixed endianness. It turns out that MS SQL Server is using its own representation for GUID with a mixed endianness. In this blind patch we attempt to parse the binary vector in the right way when building our internal representation of an UUID, before making a string out of it for Postgres, which doesn't use the same mixed-endianness format. Fixes #910. Maybe? --- src/utils/transforms.lisp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/utils/transforms.lisp b/src/utils/transforms.lisp index 4d77c71..750add8 100644 --- a/src/utils/transforms.lisp +++ b/src/utils/transforms.lisp @@ -350,10 +350,40 @@ (t date-string-or-integer))))))) +;;; +;;; MS SQL Server GUID binary representation is a mix of endianness, as +;;; documented at +;;; https://dba.stackexchange.com/questions/121869/sql-server-uniqueidentifier-guid-internal-representation +;;; and +;;; https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding. +;;; +;;; "Other systems, notably Microsoft's marshalling of UUIDs in their +;;; COM/OLE libraries, use a mixed-endian format, whereby the first three +;;; components of the UUID are little-endian, and the last two are +;;; big-endian." +;;; +;;; So here we steal some code from the UUID lib and make it compatible with +;;; this strange mix of endianness for SQL Server. +;;; +(defmacro arr-to-bytes-rev (from to array) + "Helper macro used in byte-array-to-uuid." + `(loop for i from ,to downto ,from + with res = 0 + do (setf (ldb (byte 8 (* 8 (- i ,from))) res) (aref ,array i)) + finally (return res))) + (defun sql-server-uniqueidentifier-to-uuid (id) (declare (type (or null (array (unsigned-byte 8) (16))) id)) (when id - (format nil "~a" (uuid:byte-array-to-uuid id)))) + (let ((uuid + (make-instance 'uuid:uuid + :time-low (arr-to-bytes-rev 0 3 id) + :time-mid (arr-to-bytes-rev 4 5 id) + :time-high (arr-to-bytes-rev 6 7 id) + :clock-seq-var (aref id 8) + :clock-seq-low (aref id 9) + :node (uuid::arr-to-bytes 10 15 id)))) + (princ-to-string uuid))))) (defun unix-timestamp-to-timestamptz (unixtime-string) "Takes a unix timestamp (seconds since beginning of 1970) and converts it