branch: elpa/pg
commit 5063ec8bb8679ba40b1423b6cc1b086cfb735803
Author: Eric Marsden <[email protected]>
Commit: Eric Marsden <[email protected]>

    Document the password-as-zero-arg-function support
---
 doc/src/API.md | 31 ++++++++++++++++++++------
 pg.el          | 68 ++++++++++++++++++++++++++++++++--------------------------
 2 files changed, 61 insertions(+), 38 deletions(-)

diff --git a/doc/src/API.md b/doc/src/API.md
index b13c47d4c8e..62b3e08d524 100644
--- a/doc/src/API.md
+++ b/doc/src/API.md
@@ -4,10 +4,10 @@
 The entry points in the pg-el library are documented below.
 
 
-    (with-pg-connection con (dbname user [password host port]) &body body)
+    (with-pg-connection-plist con (dbname user &key args) &body body)
 
 A macro which opens a TCP network connection to database `DBNAME`, executes 
the `BODY` forms then
-disconnects. See function `pg-connect` for details of the connection arguments.
+disconnects. See function `pg-connect-plist` for details of the connection 
arguments `ARGS`.
 
 
     (with-pg-connection-local con (path dbname user [password]) &body body)
@@ -23,13 +23,16 @@ A macro which executes the `BODY` forms wrapped in an SQL 
transaction. `CON` is
 database. If an error occurs during the execution of the forms, a ROLLBACK 
instruction is executed.
 
 
-    (pg-connect dbname user [password host port tls-options]) -> con
+    (pg-connect-plist dbname user &key password host port tls-options 
direct-tls server-variant protocol-version) -> con
     
 Connect to the database `DBNAME` on `HOST` (defaults to localhost) at `PORT` 
(defaults to 5432) via
-TCP/IP and authenticate as `USER` with `PASSWORD`. This library currently 
supports SCRAM-SHA-256
-authentication (the default method from PostgreSQL version 14 onwards), MD5 
authentication and
-cleartext password authentication. This function also sets the output date 
type to `ISO` and
-initializes our type parser tables.
+TCP/IP and authenticate as `USER` with `PASSWORD`. `PASSWORD` may be a string, 
or a zero-argument
+lambda function which returns the password as a string (this makes it possible 
to use the
+auth-source functionality in Emacs). This library currently supports 
SCRAM-SHA-256 authentication
+(the default method from PostgreSQL version 14 onwards), MD5 authentication 
and cleartext password
+authentication.
+
+This function also sets the output date type to `ISO` and initializes our type 
parser tables.
 
 If `tls-options` is non-NIL, attempt to establish an encrypted connection to 
PostgreSQL by
 passing `tls-options` to Emacs function `gnutls-negotiate`. `tls-options` is a 
Common-Lisp style
@@ -49,6 +52,20 @@ form
 where `key` is the filename of the client certificate private key and `cert` 
is the filename of the
 client certificate. These are passed to GnuTLS.
 
+If argument `direct-tls` is non-NIL, attempt to establish a “direct” TLS 
connection to PostgreSQL,
+as supported since PostgreSQL version 18. This saves a few network packets 
during the establishment
+of a network connection. This connection mode uses ALPN and requires ALPN 
support in your Emacs (in
+testing as of Emacs 30).
+
+If argument `server-variant` is non-NIL, force the detected value of 
`pgcon-server-variant` to the
+specified value. This may be needed for some PostgreSQL variants that we are 
not able to identify
+via their version string and startup options, but for which we need to 
implement workarounds (the
+primary culprit is currently Clickhouse).
+
+If `protocol-version` is non-NIL, it should be a `(major-version . 
minor-version)` cons representing
+the version of the PostgreSQL wire protocol to use. Currently `major-version` 
should be 3 and
+`minor-version` should be 0 or 2.
+
 
 
     (pg-connect-local path dbname user [password]) -> con
diff --git a/pg.el b/pg.el
index 28b0f9edc98..c5a9d2858c5 100644
--- a/pg.el
+++ b/pg.el
@@ -796,14 +796,14 @@ Uses database DBNAME, user USER and password PASSWORD."
 
           ;; AUTH_REQ_CLEARTEXT_PASSWORD
           (3
-          ;; send a PasswordMessage
-          (let ((actual-password (if (functionp password)
-                                    (funcall password)
-                                  password)))
-            (pg-send-char con ?p)
-            (pg-send-uint con (+ 5 (length actual-password)) 4)
-            (pg-send-string con actual-password))
-          (pg-flush con))
+           ;; send a PasswordMessage
+           (let ((password-string (if (functionp password)
+                                      (funcall password)
+                                    password)))
+             (pg-send-char con ?p)
+             (pg-send-uint con (+ 5 (length password-string)) 4)
+             (pg-send-string con password-string))
+           (pg-flush con))
 
          ;; AUTH_REQ_CRYPT
          (4
@@ -926,9 +926,10 @@ Uses database DBNAME, user USER and password PASSWORD."
                             (server-variant nil)
                             (protocol-version (cons 3 0)))
   "Initiate a connection with the PostgreSQL backend over TCP.
-Connect to the database DBNAME with the username USER, on PORT of
-HOST, providing PASSWORD if necessary. Return a connection to the
-database (as an opaque type). PORT defaults to 5432, HOST to
+Connect to the database DBNAME with the username USER, on PORT of HOST,
+providing PASSWORD if necessary. PASSWORD may be either a string or a
+zero-argument function which returns a string. Return a connection to
+the database (as an opaque type). PORT defaults to 5432, HOST to
 \"localhost\", and PASSWORD to an empty string.
 
 If TLS-OPTIONS is non-NIL, attempt to establish an encrypted connection
@@ -1064,8 +1065,9 @@ to use the updated protocol features introduced with 
PostgreSQL version
                              (tls-options nil)
                              (server-variant nil))
   "Initiate a connection with the PostgreSQL backend over TCP.
-Connect to the database DBNAME with the username USER, on PORT of
-HOST, providing PASSWORD if necessary. Return a connection to the
+Connect to the database DBNAME with the username USER, on PORT of HOST,
+providing PASSWORD if necessary. PASSWORD may be a either string, or a
+zero-argument function that returns a string. Return a connection to the
 database (as an opaque type). PORT defaults to 5432, HOST to
 \"localhost\", and PASSWORD to an empty string. If TLS-OPTIONS is
 non-NIL, attempt to establish an encrypted connection to PostgreSQL
@@ -1098,11 +1100,12 @@ you would like to run specific code in
                                         (port 5432)
                                         (tls-options nil))
   "Initiate a direct TLS connection with the PostgreSQL backend over TCP.
-Connect to the database DBNAME with the username USER, on PORT of
-HOST, providing PASSWORD if necessary. Return a connection to the
-database (as an opaque type). PORT defaults to 5432, HOST to
-\"localhost\", and PASSWORD to an empty string. The TLS-OPTIONS
-are passed to GnuTLS."
+Connect to the database DBNAME with the username USER, on PORT of HOST,
+providing PASSWORD if necessary. PASSWORD may be either a string, or a
+zero-argument function that returns a string. Return a connection to
+the database (as an opaque type). PORT defaults to 5432, HOST to
+\"localhost\", and PASSWORD to an empty string. The TLS-OPTIONS are
+passed to GnuTLS."
   (declare (obsolete pg-connect-plist "2025"))
   (pg-connect-plist dbname user
                     :password password
@@ -1114,8 +1117,9 @@ are passed to GnuTLS."
 (cl-defun pg-connect-local (path dbname user &optional (password ""))
   "Initiate a connection with the PostgreSQL backend over local Unix socket 
PATH.
 Connect to the database DBNAME with the username USER, providing
-PASSWORD if necessary. Return a connection to the database (as an
-opaque type). PASSWORD defaults to an empty string."
+PASSWORD if necessary. PASSWORD may be either a string, or a
+zero-argument function that returns a string. Return a connection to the
+database (as an opaque type). PASSWORD defaults to an empty string."
   (let* ((buf (generate-new-buffer " *PostgreSQL*"))
          (process (make-network-process :name "postgres"
                                         :buffer buf
@@ -3514,12 +3518,13 @@ Respects floating-point infinities and NaN."
 ;; hash = ′md5′ + md5(pwdhash + salt).hexdigest()
 (defun pg-do-md5-authentication (con user password)
   "Attempt MD5 authentication with PostgreSQL database over connection CON.
-Authenticate as USER with PASSWORD."
-  (let* ((actual-password (if (functionp password)
-                             (funcall password)
-                           password))
+Authenticate as USER with PASSWORD, which is either a string or a
+zero-argument function that returns a string."
+  (let* ((password-string (if (functionp password)
+                              (funcall password)
+                            password))
          (salt (pg-read-chars con 4))
-         (pwdhash (md5 (concat actual-password user)))
+         (pwdhash (md5 (concat password-string user)))
          (hash (concat "md5" (md5 (concat pwdhash salt)))))
     (pg-send-char con ?p)
     (pg-send-uint con (+ 5 (length hash)) 4)
@@ -3592,7 +3597,7 @@ Authenticate as USER with PASSWORD."
 ;; https://www.rfc-editor.org/rfc/rfc7677
 (defun pg-do-scram-sha256-authentication (con user password)
   "Attempt SCRAM-SHA-256 authentication with PostgreSQL over connection CON.
-Authenticate as USER with PASSWORD."
+Authenticate as USER with PASSWORD, a string."
   (let* ((mechanism "SCRAM-SHA-256")
          (client-nonce (or pg--*force-client-nonce*
                            (apply #'string (cl-loop for i below 32 collect (+ 
?A (random 25))))))
@@ -3686,17 +3691,18 @@ Authenticate as USER with PASSWORD."
 
 (defun pg-do-sasl-authentication (con user password)
   "Attempt SASL authentication with PostgreSQL over connection CON.
-Authenticate as USER with PASSWORD."
-  (let ((actual-password (if (functionp password)
-                            (funcall password)
-                          password))
+Authenticate as USER with PASSWORD, which is either a string or a
+zero-argument function that returns a string."
+  (let ((password-string (if (functionp password)
+                             (funcall password)
+                           password))
         (mechanisms (list)))
     ;; read server's list of preferered authentication mechanisms
     (cl-loop for mech = (pg-read-string con 4096)
              while (not (zerop (length mech)))
              do (push mech mechanisms))
     (if (member "SCRAM-SHA-256" mechanisms)
-        (pg-do-scram-sha256-authentication con user actual-password)
+        (pg-do-scram-sha256-authentication con user password-string)
       (let ((msg (format "Can't handle any of SASL mechanisms %s" mechanisms)))
         (signal 'pg-protocol-error (list msg))))))
 

Reply via email to