branch: elpa/pg
commit 0fbddea19021bfbe66b2dbdb4d7c7de0c0b10d9a
Merge: 845d77f1bb af414f118c
Author: Eric Marsden <[email protected]>
Commit: GitHub <[email protected]>

    Merge pull request #30 from LuciusChen/parse-array-dimension-prefixes
    
    Parse array values with dimension prefixes
---
 CHANGELOG.md    |   3 ++
 pg.el           | 124 ++++++++++++++++++++++++--------------------------------
 test/test-pg.el |   6 ++-
 3 files changed, 60 insertions(+), 73 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 079ce7ceb1..eaa4bf2b00 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
 
 ## [Unreleased]
 
+- Parse one-dimensional array values that PostgreSQL returns with explicit
+  dimension bounds, such as `[-33:-31]={100,200,300}`.
+
 - Expose the latest `ReadyForQuery` transaction status on connections via
   `pgcon-transaction-status`.
 
diff --git a/pg.el b/pg.el
index fbf88a0108..6557729d3d 100644
--- a/pg.el
+++ b/pg.el
@@ -3107,21 +3107,29 @@ Return nil if the extension could not be loaded."
 ;; levels of {} marking the extra dimensions.
 ;; See https://www.postgresql.org/docs/current/arrays.html
 
+(defun pg--array-contents (str error-message)
+  "Return the contents of a PostgreSQL array value STR.
+ERROR-MESSAGE is used if STR does not have the expected array syntax."
+  (when (string-match "\\`\\(?:\\[[+-]?[0-9]+:[+-]?[0-9]+\\]\\)+=" str)
+    (setq str (substring str (match-end 0))))
+  (let ((len (string-bytes str)))
+    (unless (and (> len 1)
+                 (eql (aref str 0) ?{)
+                 (eql (aref str (1- len)) ?}))
+      (signal 'pg-protocol-error (list error-message)))
+    (cl-subseq str 1 (- len 1))))
+
 (defun pg-intarray-parser (str _encoding)
   "Parse PostgreSQL value STR as an array of integers."
   (cl-flet ((parse-int (str)
               (if (string= "NULL" str)
                   pg-null-marker
                 (cl-parse-integer str))))
-    (let ((len (string-bytes str)))
-      (unless (and (eql (aref str 0) ?{)
-                   (eql (aref str (1- len)) ?}))
-        (signal 'pg-protocol-error (list "Unexpected format for int array")))
-      (let ((maybe-items (cl-subseq str 1 (- len 1))))
-        (if (zerop (string-bytes maybe-items))
-            (vector)
-          (let ((items (split-string maybe-items ",")))
-            (apply #'vector (mapcar #'parse-int items))))))))
+    (let ((maybe-items (pg--array-contents str "Unexpected format for int 
array")))
+      (if (zerop (string-bytes maybe-items))
+          (vector)
+        (let ((items (split-string maybe-items ",")))
+          (apply #'vector (mapcar #'parse-int items)))))))
 
 (pg-register-parser "_int2" #'pg-intarray-parser)
 (pg-register-parser "_int2vector" #'pg-intarray-parser)
@@ -3130,15 +3138,11 @@ Return nil if the extension could not be loaded."
 
 (defun pg-floatarray-parser (str _encoding)
   "Parse PostgreSQL value STR as an array of floats."
-  (let ((len (string-bytes str)))
-    (unless (and (eql (aref str 0) ?{)
-                 (eql (aref str (1- len)) ?}))
-      (signal 'pg-protocol-error (list "Unexpected format for float array")))
-    (let ((maybe-items (cl-subseq str 1 (- len 1))))
-      (if (zerop (string-bytes maybe-items))
-          (vector)
-        (let ((items (split-string maybe-items ",")))
-          (apply #'vector (mapcar (lambda (x) (pg-float-parser x nil)) 
items)))))))
+  (let ((maybe-items (pg--array-contents str "Unexpected format for float 
array")))
+    (if (zerop (string-bytes maybe-items))
+        (vector)
+      (let ((items (split-string maybe-items ",")))
+        (apply #'vector (mapcar (lambda (x) (pg-float-parser x nil)) 
items))))))
 
 (pg-register-parser "_float4" #'pg-floatarray-parser)
 (pg-register-parser "_float8" #'pg-floatarray-parser)
@@ -3146,29 +3150,21 @@ Return nil if the extension could not be loaded."
 
 (defun pg-boolarray-parser (str _encoding)
   "Parse PostgreSQL value STR as an array of boolean values."
-  (let ((len (string-bytes str)))
-    (unless (and (eql (aref str 0) ?{)
-                 (eql (aref str (1- len)) ?}))
-      (signal 'pg-protocol-error (list "Unexpected format for bool array")))
-    (let ((maybe-items (cl-subseq str 1 (- len 1))))
-      (if (zerop (string-bytes maybe-items))
-          (vector)
-        (let ((items (split-string maybe-items ",")))
-          (apply #'vector (mapcar (lambda (x) (pg-bool-parser x nil)) 
items)))))))
+  (let ((maybe-items (pg--array-contents str "Unexpected format for bool 
array")))
+    (if (zerop (string-bytes maybe-items))
+        (vector)
+      (let ((items (split-string maybe-items ",")))
+        (apply #'vector (mapcar (lambda (x) (pg-bool-parser x nil)) items))))))
 
 (pg-register-parser "_bool" #'pg-boolarray-parser)
 
 (defun pg-chararray-parser (str encoding)
   "Parse PostgreSQL value STR as an array of characters using ENCODING."
-  (let ((len (string-bytes str)))
-    (unless (and (eql (aref str 0) ?{)
-                 (eql (aref str (1- len)) ?}))
-      (signal 'pg-protocol-error (list "Unexpected format for char array")))
-    (let ((maybe-items (cl-subseq str 1 (- len 1))))
-      (if (zerop (string-bytes maybe-items))
-          (vector)
-        (let ((items (split-string maybe-items ",")))
-          (apply #'vector (mapcar (lambda (x) (pg-char-parser x encoding)) 
items)))))))
+  (let ((maybe-items (pg--array-contents str "Unexpected format for char 
array")))
+    (if (zerop (string-bytes maybe-items))
+        (vector)
+      (let ((items (split-string maybe-items ",")))
+        (apply #'vector (mapcar (lambda (x) (pg-char-parser x encoding)) 
items))))))
 
 (pg-register-parser "_char" #'pg-chararray-parser)
 (pg-register-parser "_bpchar" #'pg-chararray-parser)
@@ -3176,15 +3172,11 @@ Return nil if the extension could not be loaded."
 (defun pg-textarray-parser (str encoding)
   "Parse PostgreSQL value STR as an array of TEXT values.
 Uses text encoding ENCODING."
-  (let ((len (string-bytes str)))
-    (unless (and (eql (aref str 0) ?{)
-                 (eql (aref str (1- len)) ?}))
-      (signal 'pg-protocol-error (list "Unexpected format for text array")))
-    (let ((maybe-items (cl-subseq str 1 (- len 1))))
-      (if (zerop (string-bytes maybe-items))
-          (vector)
-        (let ((items (split-string maybe-items ",")))
-          (apply #'vector (mapcar (lambda (x) (pg-text-parser x encoding)) 
items)))))))
+  (let ((maybe-items (pg--array-contents str "Unexpected format for text 
array")))
+    (if (zerop (string-bytes maybe-items))
+        (vector)
+      (let ((items (split-string maybe-items ",")))
+        (apply #'vector (mapcar (lambda (x) (pg-text-parser x encoding)) 
items))))))
 
 (pg-register-parser "_text" #'pg-textarray-parser)
 (pg-register-parser "_varchar" #'pg-textarray-parser)
@@ -3231,15 +3223,11 @@ Uses text encoding ENCODING."
 (defun pg-uuidarray-parser (str encoding)
   "Parse PostgreSQL value STR as an array of UUID values.
 Uses text encoding ENCODING."
-  (let ((len (string-bytes 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 (string-bytes maybe-items))
-          (vector)
-        (let ((items (split-string maybe-items ",")))
-          (apply #'vector (mapcar (lambda (x) (pg-text-parser x encoding)) 
items)))))))
+  (let ((maybe-items (pg--array-contents str "Unexpected format for UUID 
array")))
+    (if (zerop (string-bytes 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)
 
@@ -3257,15 +3245,11 @@ Uses text encoding ENCODING."
 
 (defun pg-datearr-parser (str _encoding)
   "Parse PostgreSQL value STR as an array of date values."
-  (let ((len (string-bytes str)))
-    (unless (and (eql (aref str 0) ?{)
-                 (eql (aref str (1- len)) ?}))
-      (signal 'pg-protocol-error (list "Unexpected format for array")))
-    (let ((maybe-items (cl-subseq str 1 (- len 1))))
-      (if (zerop (string-bytes maybe-items))
-          (vector)
-        (let ((items (split-string maybe-items ",")))
-          (apply #'vector (mapcar (lambda (x) (pg-date-parser x nil)) 
items)))))))
+  (let ((maybe-items (pg--array-contents str "Unexpected format for array")))
+    (if (zerop (string-bytes maybe-items))
+        (vector)
+      (let ((items (split-string maybe-items ",")))
+        (apply #'vector (mapcar (lambda (x) (pg-date-parser x nil)) items))))))
 
 (pg-register-parser "_date" #'pg-datearr-parser)
 
@@ -3325,15 +3309,11 @@ Uses text encoding ENCODING."
 ;; This is usable for time, timespan etc. types that we currently parse as 
strings.
 (defun pg-timearr-parser (str _encoding)
   "Parse PostgreSQL value STR as an array of time or date values."
-  (let ((len (string-bytes str)))
-    (unless (and (eql (aref str 0) ?{)
-                 (eql (aref str (1- len)) ?}))
-      (signal 'pg-protocol-error (list "Unexpected format for array")))
-    (let ((maybe-items (cl-subseq str 1 (- len 1))))
-      (if (zerop (string-bytes maybe-items))
-          (vector)
-        (let ((items (split-string maybe-items ",")))
-          (apply #'vector (mapcar (lambda (x) (pg-text-parser x nil)) 
items)))))))
+  (let ((maybe-items (pg--array-contents str "Unexpected format for array")))
+    (if (zerop (string-bytes maybe-items))
+        (vector)
+      (let ((items (split-string maybe-items ",")))
+        (apply #'vector (mapcar (lambda (x) (pg-text-parser x nil)) items))))))
 
 (pg-register-parser "_time" #'pg-timearr-parser)
 (pg-register-parser "_timetz" 'pg-timearr-parser)
diff --git a/test/test-pg.el b/test/test-pg.el
index ba38dc81e5..95d1e5c3df 100755
--- a/test/test-pg.el
+++ b/test/test-pg.el
@@ -1973,7 +1973,11 @@ bar$$"))))
     (should (equal (vector) (scalar "SELECT (ARRAY[10,11,12])[5:42]")))
     (let* ((res (pg-exec con "SELECT 
generate_subscripts('[-33:-31]={100,200,300}'::int[], 1)"))
            (row (pg-result res :tuples)))
-      (should (equal row '((-33) (-32) (-31)))))))
+      (should (equal row '((-33) (-32) (-31)))))
+    (should (equal (vector 100 200 300)
+                   (scalar "SELECT '[-33:-31]={100,200,300}'::int[]")))
+    (should (equal (vector "foo" "bar")
+                   (scalar "SELECT '[0:1]={foo,bar}'::text[]")))))
 
 ;; TODO: we do not currently handle multidimension arrays correctly
 ;; (should (equal (vector (vector 4 5) (vector 6 7))

Reply via email to