Attached is a patch that adds Postmodern support for asynchronous
notifications, as documented here:

  http://www.postgresql.org/docs/current/static/sql-listen.html

  http://www.postgresql.org/docs/current/static/sql-notify.html

  http://www.postgresql.org/docs/current/static/sql-unlisten.html

and

  
http://www.postgresql.org/docs/current/interactive/protocol-message-formats.html
  (under "NotificationResponse")

It updates MESSAGE-CASE to watch for async notifications and, by
default, issue a warning that looks very similar to what the psql client
displays for async notifications:

  WARNING:
     Asynchornous notification "lisp" received from server process with
     PID 22063.

It also adds a blocking function, WAIT-FOR-NOTIFICATION, that adds a
handler-bind around the warning and returns the notification data when a
notification is received.

The patch isn't complete, though -- I wasn't sure how best to integrate
the new public function, condition, and condition accessors into the
documentation. How should I proceed?

Zach
diff --git a/cl-postgres/package.lisp b/cl-postgres/package.lisp
index 43468dc..71cae30 100644
--- a/cl-postgres/package.lisp
+++ b/cl-postgres/package.lisp
@@ -16,6 +16,7 @@
            #:reopen-database
            #:database-open-p
            #:close-database
+           #:wait-for-notification
            #:exec-query
            #:prepare-query
            #:exec-prepared
@@ -29,6 +30,9 @@
            #:log-query
            #:vector-row-reader
            #:alist-row-reader
+           #:postgresql-notification
+           #:postgresql-notification-pid
+           #:postgresql-notification-condition
            #:postgresql-warning
            #:ignore-row-reader
            #:*sql-readtable*
diff --git a/cl-postgres/protocol.lisp b/cl-postgres/protocol.lisp
index a047732..4cc169b 100644
--- a/cl-postgres/protocol.lisp
+++ b/cl-postgres/protocol.lisp
@@ -36,6 +36,9 @@ Cases for error and warning messages are always added."
                               (type (unsigned-byte 32) ,size-name)
                               (ignorable ,size-name))
                      (case ,char-name
+                       (#.(char-code #\A)
+                          (get-notification ,socket-name)
+                          (,iter-name))
                        (#.(char-code #\E) (get-error ,socket-name))
                        (#.(char-code #\S) ;; ParameterStatus: read and continue
                           (update-parameter ,socket-name)
@@ -72,6 +75,25 @@ and put them in an alist."
         :until (zerop type)
         :collect (cons (code-char type) (read-str socket))))
 
+(define-condition postgresql-notification (simple-warning)
+  ((pid :initarg :pid :accessor postgresql-notification-pid)
+   (condition :initarg :condition :accessor 
postgresql-notification-condition)))
+
+(defun get-notification (socket)
+  "Read an asynchronous notification message from the socket and
+signal a condition for it."
+  (let ((pid (read-int4 socket))
+        (condition (read-str socket))
+        (additional-info (read-str socket)))
+    ;; Per the documentation, the additional-info string is always empty.
+    (declare (ignore additional-info))
+    (warn 'postgresql-notification
+          :pid pid
+          :condition condition
+          :format-control "Asynchornous notification ~s received from ~
+                           server process with PID ~D."
+          :format-arguments (list condition pid))))
+
 (defun get-error (socket)
   "Read an error message from the socket and raise the corresponding
 database-error condition."
diff --git a/cl-postgres/public.lisp b/cl-postgres/public.lisp
index dbdfcde..a3b0b9d 100644
--- a/cl-postgres/public.lisp
+++ b/cl-postgres/public.lisp
@@ -169,6 +169,17 @@ offering a :reconnect restart."
                              (,body-name)))))
       (,body-name)))))
 
+(defun wait-for-notification (connection)
+  "Perform a blocking wait for asynchronous notification. Return the
+condition string and notifying pid as multiple values."
+  (block nil
+    (with-reconnect-restart connection
+      (handler-bind ((postgresql-notification
+                      (lambda (c)
+                        (return (values (postgresql-notification-condition c)
+                                        (postgresql-notification-pid c))))))
+        (message-case (connection-socket connection))))))
+
 (defun exec-query (connection query &optional (row-reader 'ignore-row-reader))
   "Execute a query string and apply the given row-reader to the
 result."
_______________________________________________
postmodern-devel mailing list
postmodern-devel@common-lisp.net
http://common-lisp.net/cgi-bin/mailman/listinfo/postmodern-devel

Reply via email to