From 78b42cc4ae45ccd1409f90420082c9db646b56ff Mon Sep 17 00:00:00 2001 From: Dimitri Fontaine Date: Fri, 22 Nov 2013 11:01:20 +0100 Subject: [PATCH] Properly handle blob formats, converting then to PostgreSQL bytea input. --- src/sources/mysql-cast-rules.lisp | 24 +++++++++++++++--------- src/transforms.lisp | 28 +++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/sources/mysql-cast-rules.lisp b/src/sources/mysql-cast-rules.lisp index 0ba4d66..f7373f1 100644 --- a/src/sources/mysql-cast-rules.lisp +++ b/src/sources/mysql-cast-rules.lisp @@ -93,16 +93,22 @@ :target (:type "varchar" :drop-typemod nil)) ;; - ;; cl-mysql and postmodern are adapting binary values as a simple-array - ;; (or vector) of ‘(UNSIGNED-BYTE 8), so there should be no other - ;; explicit conversion to do here. + ;; cl-mysql returns binary values as a simple-array of bytes (as in + ;; ‘(UNSIGNED-BYTE 8)), that we then need to represent as proper + ;; PostgreSQL bytea input. ;; - (:source (:type "binary") :target (:type "bytea")) - (:source (:type "varbinary") :target (:type "bytea")) - (:source (:type "tinyblob") :target (:type "bytea")) - (:source (:type "blob") :target (:type "bytea")) - (:source (:type "mediumblob") :target (:type "bytea")) - (:source (:type "longblob") :target (:type "bytea")) + (:source (:type "binary") :target (:type "bytea") + :using pgloader.transforms::byte-vector-to-bytea) + (:source (:type "varbinary") :target (:type "bytea") + :using pgloader.transforms::byte-vector-to-bytea) + (:source (:type "tinyblob") :target (:type "bytea") + :using pgloader.transforms::byte-vector-to-bytea) + (:source (:type "blob") :target (:type "bytea") + :using pgloader.transforms::byte-vector-to-bytea) + (:source (:type "mediumblob") :target (:type "bytea") + :using pgloader.transforms::byte-vector-to-bytea) + (:source (:type "longblob") :target (:type "bytea") + :using pgloader.transforms::byte-vector-to-bytea) (:source (:type "datetime" :default "0000-00-00 00:00:00" :not-null t) :target (:type "timestamptz" :drop-default t :drop-not-null t) diff --git a/src/transforms.lisp b/src/transforms.lisp index c1cec4d..a1e6e8b 100644 --- a/src/transforms.lisp +++ b/src/transforms.lisp @@ -21,7 +21,8 @@ convert-mysql-point float-to-string set-to-enum-array - right-trim)) + right-trim + byte-vector-to-bytea)) ;;; @@ -143,3 +144,28 @@ "Remove whitespaces at end of STRING." (declare (type simple-string string)) (string-right-trim '(#\Space) string)) + +(defun byte-vector-to-bytea (vector) + "Transform a simple array of unsigned bytes to the PostgreSQL bytea + representation as documented at + http://www.postgresql.org/docs/9.3/interactive/datatype-binary.html + + Note that we choose here the bytea Hex Format." + (declare (type simple-array vector)) + (let ((hex-digits "0123456789abcdef") + (bytea (make-array (+ 2 (* 2 (length vector))) + :initial-element #\0 + :element-type 'standard-char))) + + ;; The entire string is preceded by the sequence \x (to distinguish it + ;; from the escape format). + (setf (aref bytea 0) #\\) + (setf (aref bytea 1) #\x) + + (loop for pos from 2 by 2 + for byte across vector + do (let ((high (ldb (byte 4 4) byte)) + (low (ldb (byte 4 0) byte))) + (setf (aref bytea pos) (aref hex-digits high)) + (setf (aref bytea (+ pos 1)) (aref hex-digits low))) + finally (return bytea))))