Special pg-el features
Handling parameter changes
The PostgreSQL backend informs connected clients when certain server parameters change, by sending
them a special ParameterStatus
message. These notifications are sent for GUC_REPORT
parameters,
which include the client_encoding
, the DateStyle
, TimeZone
, server_encoding
,
in_hot_standby
and is_superuser
. You can register your interest in these messages by adding a
handler function to pg-parameter-change-functions
. Each of these handler functions will be called
when such a message is received, with three arguments: the connection to PostgreSQL, the parameter
name and the parameter value.
These messages are sent asynchronously.
ELISP> (defun handle-tz-change (_con name value)
(when (string= name "TimeZone")
(message "New timezone in PostgreSQL is %s" value)))
handle-tz-change
ELISP> (cl-pushnew #'handle-tz-change pg-parameter-change-functions)
(handle-tz-change pg-handle-parameter-client-encoding)
ELISP> (pg-result (pg-exec *pg* "SET SESSION TIME ZONE 'Europe/Paris'") :status)
"SET"
ELISP> (pg-result (pg-exec *pg* "SET SESSION TIME ZONE 'America/Chicago'") :status)
"SET"
You should see either one or two messages announcing a parameter change (the first statement won’t generate a ParameterStatus message if the time zone was already set to Europe/Paris).
Handling asynchronous notifications
PostgreSQL has an asynchronous notification functionality based on the LISTEN and NOTIFY
commands. A client can register its
interest in a particular notification channel with the LISTEN
command, and later stop listening
with the UNLISTEN
command. All clients listening on a particular channel will be notified
asynchronously when a NOTIFY
command with that channel name is executed by any client. A “payload”
string can be passed to communicate additional data to the listeners. In pg-el you can register
functions to be called when an asynchronous notification is received by adding them to the
pg-handle-notice-functions
. Each handler function is called with a single argument, the notice, in
the form of a pgerror
struct.
PostgreSQL will signal an asynchronous notification for a DROP TABLE IF EXISTS
command that attempts
to remove a table that doesn’t exist, as a form of warning message. We can register our interest in
this message by locally binding the pg-handle-notice-functions
variable.
ELISP> (defun deity-p (notif)
;; the notification message will be localized, but should contain the table name
(when (cl-search "deity" (pgerror-message notif))
(message "Indeed")))
ELISP> (let ((pg-handle-notice-functions (list #'deity-p)))
(pg-result (pg-exec *pg* "DROP TABLE IF EXISTS deity") :status))
"DROP TABLE"
You should see the message in the minibuffer.
This example illustrates the use of NOTIFY and LISTEN. It’s obviously not very useful with a single client; real applications would involve multiple event consumers and possibly also multiple event producers. This functionality can be used to implement simple publish-subscribe communication patterns, with PostgreSQL serving as an event broker.
(cl-flet ((notification-handler (channel payload)
(message "Async notification on %s: %s" channel payload)))
(pg-add-notification-handler *pg* #'notification-handler)
(pg-exec *pg* "LISTEN yourheart")
(pg-exec *pg* "NOTIFY yourheart, 'foobles'")
(pg-exec *pg* "SELECT 'ignored'")
(pg-exec *pg* "NOTIFY yourheart, 'bazzles'")
(sleep-for 10)
(pg-exec *pg* "SELECT 'ignored'")
(pg-exec *pg* "NOTIFY yourheart")
(pg-exec *pg* "SELECT 'ignored'")
;; The function pg_notify is an alternative to the LISTEN statement, and more flexible if your
;; channel name is determined by a variable.
(pg-exec *pg* "SELECT pg_notify('yourheart', 'leaving')")
(pg-exec *pg* "SELECT 'ignored'")
(pg-exec *pg* "UNLISTEN yourheart")
(pg-exec *pg* "NOTIFY yourheart, 'Et redit in nihilum quod fuit ante nihil.'")))