branch: elpa/pg
commit b03cdf7b7245f21e0218056824744299cc3f5e87
Author: Eric Marsden <eric.mars...@risk-engineering.org>
Commit: Eric Marsden <eric.mars...@risk-engineering.org>

    Add parsing and serialization support for the  data type
---
 CHANGELOG.md    |  2 ++
 pg.el           | 38 +++++++++++++++++++++++++++++++++++++-
 test/test-pg.el | 26 ++++++++++++++++++++++----
 3 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cec95a03bb6..8e7b593f8a8 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,8 @@
 
 - Add serialization support for the `_varchar` data type.
 
+- Add parsing and serialization support for the `_uuid` data type.
+
 - Add workarounds and detection code for the CedarDB PostgreSQL variant.
 
 - New error classes `pg-duplicate-table` and `pg-duplicate-column`, subclasses 
of
diff --git a/pg.el b/pg.el
index 0a4520f5174..f4742d27481 100644
--- a/pg.el
+++ b/pg.el
@@ -2546,6 +2546,8 @@ the PostgreSQL connection CON."
 (pg-register-parser "text" #'pg-text-parser)
 (pg-register-parser "varchar" #'pg-text-parser)
 (pg-register-parser "xml" #'pg-text-parser)
+
+;; TODO; could verify the UUID syntax here, but it seems unnecessary to double 
guess PostgreSQL.
 (pg-register-parser "uuid" #'pg-text-parser)
 
 (pg-register-parser "bytea"
@@ -2787,6 +2789,21 @@ Uses text encoding ENCODING."
 
 (pg-register-parser "money" #'pg-text-parser)
 
+(defun pg-uuidarray-parser (str encoding)
+  "Parse PostgreSQL value STR as an array of UUID values.
+Uses text encoding ENCODING."
+  (let ((len (length str)))
+    (unless (and (eql (aref str 0) ?{)
+                 (eql (aref str (1- len)) ?}))
+      (signal 'pg-protocol-error (list "Unexpected format for UUID array")))
+    (let ((maybe-items (cl-subseq str 1 (- len 1))))
+      (if (zerop (length maybe-items))
+          (vector)
+        (let ((items (split-string maybe-items ",")))
+          (apply #'vector (mapcar (lambda (x) (pg-text-parser x encoding)) 
items)))))))
+
+(pg-register-parser "_uuid" #'pg-uuidarray-parser)
+
 ;; format for ISO dates is "1999-10-24"
 (defun pg-date-parser (str _encoding)
   "Parse PostgreSQL value STR as a date."
@@ -3160,9 +3177,28 @@ Respects floating-point infinities and NaN."
     (concat "{" (string-join (mapcar (lambda (v) (pg--serialize-float v 
encoding)) vector) ",") "}")))
 
 (pg-register-textual-serializer "_float8"
-   (lambda (vector encoding)
+  (lambda (vector encoding)
      (concat "{" (string-join (mapcar (lambda (v) (pg--serialize-float v 
encoding)) vector) ",") "}")))
 
+(pg-register-textual-serializer "_uuid"
+  (lambda (vector encoding)
+    (let ((uuid-rx (rx string-start
+                       (group (repeat 8 xdigit)) ?-
+                       (group (repeat 4 xdigit)) ?-
+                       (group (repeat 4 xdigit)) ?-
+                       (group (repeat 4 xdigit)) ?-
+                       (group (repeat 12 xdigit))
+                       string-end)))
+      (with-temp-buffer
+        (insert "{")
+        (cl-loop
+         for uuid across vector
+         do (unless (string-match uuid-rx uuid)
+              (pg-signal-type-error "Expecting a UUID, got %s" uuid))
+         (insert uuid ","))
+        (delete-char -1)                    ; the last comma
+        (insert "}")
+        (buffer-string)))))
 
 
 ;; pwdhash = md5(password + username).hexdigest()
diff --git a/test/test-pg.el b/test/test-pg.el
index 2d423ad7199..9e1a09b685d 100755
--- a/test/test-pg.el
+++ b/test/test-pg.el
@@ -992,7 +992,19 @@ bar$$"))))
                  (rows (pg-result res :tuples)))
             (dotimes (i size)
               (should (string= (format "%04d-value" i) (cl-first (nth i 
rows)))))))
-        (pg-exec con "DROP TABLE sarray")))))
+        (pg-exec con "DROP TABLE sarray"))
+      (when (pgtest-have-table con "uuidarray")
+        (pg-exec con "DROP TABLE uuidarray"))
+      (when-let* ((sql (pgtest-massage con "CREATE TABLE uuidarray(id SERIAL 
PRIMARY KEY, val UUID[])")))
+        (pg-exec con sql)
+        (pg-exec-prepared con "INSERT INTO uuidarray(val) VALUES($1)"
+                          '((["a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" 
"c4792ecb-c00a-43a2-bd74-5b0ed551c599"] . "_uuid")))
+        (pgtest-flush-table con "uuidarray")
+        (let* ((res (pg-exec con "SELECT val FROM uuidarray"))
+               (ua (cl-first (pg-result res :tuple 0))))
+          (should (string-equal-ignore-case (aref ua 0) 
"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")))
+        (pg-exec con "DROP TABLE uuidarray")))))
+
 
 ;; Check the mixing of prepared queries, cached prepared statements, normal 
simple queries, to check
 ;; that the cache works as expected and that the backend retains prepared 
statements. TODO: should
@@ -1355,6 +1367,11 @@ bar$$"))))
     (should (string-equal-ignore-case
              "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
              (scalar "SELECT 'A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11'::uuid")))
+    (let ((uuids (scalar "SELECT '{\"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11\",
+                                   
\"c4792ecb-c00a-43a2-bd74-5b0ed551c599\"}'::uuid[]")))
+      (should (vectorp uuids))
+      (should (string-equal-ignore-case (aref uuids 0)  
"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"))
+      (should (string-equal-ignore-case (aref uuids 1) 
"c4792ecb-c00a-43a2-bd74-5b0ed551c599")))
     ;; Apparently only defined from PostgreSQL v13 onwards.
     (when (pg-function-p con "gen_random_uuid")
       (dotimes (_i 30)
@@ -1480,6 +1497,7 @@ bar$$"))))
     (should (equal (vector) (scalar "SELECT '{}'::bool[]")))
     (should (equal (vector) (scalar "SELECT '{}'::float4[]")))
     (should (equal (vector) (scalar "SELECT '{}'::float8[]")))
+    (should (equal (vector "AB1234" "4321BA") (scalar "SELECT 
'{\"AB1234\",\"4321BA\"}'::bpchar[]")))
     (let ((vec (scalar "SELECT ARRAY[3.14::float]")))
       (should (floatp (aref vec 0)))
       (should (pgtest-approx= 3.14 (aref vec 0))))
@@ -1499,9 +1517,9 @@ bar$$"))))
       ;; this is returning _bpchar.
       (should (equal (vector ?a ?b ?c) (scalar "SELECT CAST('{a,b,c}' AS 
CHAR[])"))))
     (should (equal (vector "foo" "bar") (scalar "SELECT '{foo, 
bar}'::text[]")))
-;;     (let* ((res (pg-exec-prepared con "SELECT $1" '(("{1,2,3}" . "_int4"))))
-;;            (row (pg-result res :tuple 0)))
-;;       (should (equal (vector 1 2 3) (cl-first row))))
+    (let* ((res (pg-exec-prepared con "SELECT $1" '(([1 2 3] . "_int4"))))
+           (row (pg-result res :tuple 0)))
+      (should (equal (vector 1 2 3) (cl-first row))))
     (let ((vec (scalar "SELECT ARRAY[44.3, 8999.5]")))
       (should (equal 2 (length vec)))
       (should (pgtest-approx= 44.3 (aref vec 0)))

Reply via email to