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."