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

    - Add serialization support for the `numeric` data type, for arguments to 
prepared statements.
    
    - Add serialization support for homogeneous arrays when using the extended 
query protocol. Types such as
      `text[]` (known as `_text` in the `pg_type` system table), `int4[]`, 
`float4[]`, `float8[]` can now
      be passed as function arguments.
    
    - Add workarounds and detection code for the CedarDB PostgreSQL variant.
    
    - New error classes `pg-duplicate-table` and `pg-duplicate-column`, 
subclasses of
      `pg-programming-error`.
    
    - New error class `pg-sequence-limit-exceeded`, a subclass of 
`pg-data-error`.
---
 CHANGELOG.md | 16 +++++++++++++++
 pg.el        | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1d667181a2..6441b0bbc1e 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,22 @@
 # Changelog
 
 
+## [0.58] - Unreleased
+
+- Add serialization support for the `numeric` data type, for arguments to 
prepared statements.
+
+- Add serialization support for homogeneous arrays when using the extended 
query protocol. Types such as
+  `text[]` (known as `_text` in the `pg_type` system table), `int4[]`, 
`float4[]`, `float8[]` can now
+  be passed as function arguments.
+
+- Add workarounds and detection code for the CedarDB PostgreSQL variant.
+
+- New error classes `pg-duplicate-table` and `pg-duplicate-column`, subclasses 
of
+  `pg-programming-error`.
+
+- New error class `pg-sequence-limit-exceeded`, a subclass of `pg-data-error`.
+
+
 ## [0.57] - 2025-07-30
 
 - Fix the large object functionality to work with version 3 of the 
frontend-backend wire protocol.
diff --git a/pg.el b/pg.el
index 5708405fb8f..3df1f200ce5 100644
--- a/pg.el
+++ b/pg.el
@@ -153,7 +153,9 @@ SQL queries. To avoid this overhead on establishing a 
connection, remove
 (define-error 'pg-undefined-table "PostgreSQL undefined table" 
'pg-programming-error)
 (define-error 'pg-undefined-column "PostgreSQL undefined column" 
'pg-programming-error)
 (define-error 'pg-undefined-function "PostgreSQL undefined function" 
'pg-programming-error)
+(define-error 'pg-duplicate-column "Duplicate column" 'pg-programming-error)
 (define-error 'pg-duplicate-prepared-statement "Duplicate prepared statement" 
'pg-programming-error)
+(define-error 'pg-duplicate-table "Duplicate table or sequence" 
'pg-programming-error)
 (define-error 'pg-reserved-name "PostgreSQL reserved name" 
'pg-programming-error)
 (define-error 'pg-copy-failed "PostgreSQL COPY failed" 'pg-operational-error)
 (define-error 'pg-connect-timeout "PostgreSQL connection attempt timed out" 
'pg-operational-error)
@@ -172,6 +174,7 @@ SQL queries. To avoid this overhead on establishing a 
connection, remove
 (define-error 'pg-datatype-mismatch "PostgreSQL datatype mismatch" 
'pg-data-error)
 (define-error 'pg-json-error "PostgreSQL JSON-related error" 'pg-data-error)
 (define-error 'pg-xml-error "PostgreSQL XML-related error" 'pg-data-error)
+(define-error 'pg-sequence-limit-exceeded "PostgreSQL sequence generator limit 
exceeded" 'pg-data-error)
 (define-error 'pg-integrity-constraint-violation "PostgreSQL integrity 
constraint violation" 'pg-integrity-error)
 (define-error 'pg-restrict-violation "PostgreSQL restrict violation" 
'pg-integrity-error)
 (define-error 'pg-not-null-violation "PostgreSQL not NULL violation" 
'pg-integrity-error)
@@ -494,6 +497,8 @@ Uses connection CON. The variant can be accessed by 
`pgcon-server-variant'."
               (setf (pgcon-server-variant con) 'vertica))
              ((cl-search "PolarDB " version)
               (setf (pgcon-server-variant con) 'polardb))
+             ((cl-search "CedarDB " version)
+              (setf (pgcon-server-variant con) 'cedardb))
              ;; TODO: find a better detection method for ArcadeDB
              ((string-suffix-p "/main)" version)
               (setf (pgcon-server-variant con) 'arcadedb))
@@ -583,6 +588,7 @@ presented to the user."
                         ("2201F" 'pg-floating-point-exception)
                         ("22021" 'pg-character-not-in-repertoire)
                         ((pred (lambda (v) (string-prefix-p "2203" v))) 
'pg-json-error)
+                        ("2200H" 'pg-sequence-limit-exceeded)
                         ("2200L" 'pg-xml-error)
                         ("2200M" 'pg-xml-error)
                         ("2200N" 'pg-xml-error)
@@ -610,6 +616,8 @@ presented to the user."
                         ("42601" 'pg-syntax-error)
                         ("42P01" 'pg-undefined-table)
                         ("42P05" 'pg-duplicate-prepared-statement)
+                        ("42P07" 'pg-duplicate-table)
+                        ("42701" 'pg-duplicate-column)
                         ("42703" 'pg-undefined-column)
                         ("42804" 'pg-datatype-mismatch)
                         ("42883" 'pg-undefined-function)
@@ -1517,6 +1525,12 @@ The prepared statement may be given optional NAME 
(defaults to an
 unnamed prepared statement). ARGUMENT-TYPES is a list of
 PostgreSQL type names of the form (\"int4\" \"text\" \"bool\").
 Returns the prepared statement name (a string)."
+  (when (pgcon-query-log con)
+    (with-current-buffer (pgcon-query-log con)
+      (insert query "\n")
+      (insert (format "   %s\n" argument-types)))
+    (when noninteractive
+      (message "SQL!> %s %s" query argument-types)))
   (cl-flet ((oid-for (type-name)
               ;; If we have defined a serializer for type-name and we know the 
corresponding OID, we
               ;; will be sending this type in binary form: return that OID. 
Otherwise, return the
@@ -2362,16 +2376,23 @@ PostgreSQL and Emacs. CON should no longer be used."
                      ("time" "1083")
                      ("timestamp" "1114")
                      ("timestamptz" "1184")
+                     ("bit" "1560")
+                     ("varbit" "1562")
                      ("numeric" "1700")
                      ("uuid" "2950")
                      ("jsonb" "3802")
+                     ("_xml" "143")
+                     ("_json" "199")
                      ("_bool" "1000")
+                     ("_char" "1002")
                      ("_int8" "1016")
                      ("_int2" "1005")
                      ("_int4" "1007")
+                     ("_text" "1009")
                      ("_float4" "1021")
                      ("_float8" "1022")
-                     ("_numeric" "1231")))))
+                     ("_numeric" "1231")
+                     ("_bit" "1561")))))
       (dolist (row rows)
         (let* ((typname (cl-first row))
                (oid (cl-parse-integer (cl-second row)))
@@ -3058,6 +3079,7 @@ Respects floating-point infinities and NaN."
 
 (pg-register-textual-serializer "float4" #'pg--serialize-float)
 (pg-register-textual-serializer "float8" #'pg--serialize-float)
+(pg-register-textual-serializer "numeric" #'pg--serialize-float)
 
 ;; FIXME probably we should be encoding this.
 (defun pg--serialize-json (json _encoding)
@@ -3100,6 +3122,41 @@ Respects floating-point infinities and NaN."
     (concat "[" (string-join (mapcar #'prin1-to-string v) ",") "]")))
 
 
+(pg-register-textual-serializer "_text"
+  (lambda (vector encoding)
+     (concat "{" (string-join (mapcar (lambda (v) (pg--serialize-text v 
encoding)) vector) ",") "}")))
+
+;; We currently serialize these in textual format. They could also be 
serialized in binary form as per
+;; 
https://stackoverflow.com/questions/4016412/postgresqls-libpq-encoding-for-binary-transport-of-array-data
+;; and https://doxygen.postgresql.org/array_8h_source.html
+(defun pg--serialize-intarray (vector _encoding)
+  (with-temp-buffer
+    (insert "{")
+    (cl-loop
+     for element across vector
+     do (insert (number-to-string element) ","))
+    (delete-char -1)                    ; the last comma
+    (insert "}")
+    (buffer-string)))
+
+(pg-register-textual-serializer "_int2" #'pg--serialize-intarray)
+(pg-register-textual-serializer "_int4" #'pg--serialize-intarray)
+(pg-register-textual-serializer "_int8" #'pg--serialize-intarray)
+(pg-register-textual-serializer "_numeric" #'pg--serialize-intarray)
+(pg-register-textual-serializer "_oid" #'pg--serialize-intarray)
+
+;; We don't know how to serialize floating point numbers in binary format in 
Emacs Lisp, so
+;; serialize them in textual form.
+(pg-register-textual-serializer "_float4"
+  (lambda (vector encoding)
+    (concat "{" (string-join (mapcar (lambda (v) (pg--serialize-float v 
encoding)) vector) ",") "}")))
+
+(pg-register-textual-serializer "_float8"
+   (lambda (vector encoding)
+     (concat "{" (string-join (mapcar (lambda (v) (pg--serialize-float v 
encoding)) vector) ",") "}")))
+
+
+
 ;; pwdhash = md5(password + username).hexdigest()
 ;; hash = ′md5′ + md5(pwdhash + salt).hexdigest()
 (defun pg-do-md5-authentication (con user password)
@@ -3357,6 +3414,8 @@ TABLE can be a string or a schema-qualified name. Uses 
database connection CON."
     ('questdb nil)
     ('spanner nil)
     ('ydb nil)
+    ;; As of 2025-08, CedarDB returns "Setting comments in not implemented 
yet" (sic).
+    ('cedardb nil)
     ;; Our query below using PostgreSQL system tables triggers an internal 
exception in CockroachDB,
     ;; so we use their non-standard "SHOW TABLES" query. The SHOW TABLES 
command does not accept a
     ;; WHERE clause.
@@ -3403,7 +3462,8 @@ TABLE can be a string or a schema-qualified name. Uses 
database connection CON."
      ('questdb nil)
      ('spanner nil)
      ('ydb nil)
-     ;; TheNile raises and error "command tag COMMENT unhandled"
+     ('cedardb nil)
+     ;; TheNile raises an error "command tag COMMENT unhandled"
      ('thenile nil)
      (_
       (let* ((cmt (if ,comment

Reply via email to