Add support for connecting to PostgreSQL with Unix Domain Sockets, fixing #15.

This commit is contained in:
Dimitri Fontaine 2013-11-22 17:19:48 +01:00
parent fdfcb8dde9
commit 4bc4cc53a7
4 changed files with 76 additions and 40 deletions

View File

@ -122,7 +122,16 @@ Where:
- *netloc*
Can be either a hostname in dotted notation, or an ipv4.
Can be either a hostname in dotted notation, or an ipv4, or an unix
domain socket path. Empty is the default network location, under a
system providing *unix domain socket* that method is prefered, otherwise
the *netloc* default to `localhost`.
It's possible to force the *unix domain socket* path by using the syntax
`unix:/path/to/where/the/socket/file/is`, so to force a non default
socket path and a non default port, you would have:
postgresql://unix:/tmp:54321/dbname
- *dbname*

View File

@ -244,12 +244,27 @@
(? (digit-char-p character))))
(defrule ipv4 (and ipv4-part "." ipv4-part "." ipv4-part "." ipv4-part)
(:text t))
(:lambda (ipv4)
(list :ipv4 (text ipv4))))
(defrule hostname (or ipv4 (and namestring (? (and "." hostname))))
(:text t))
;;; socket directory is unix only, so we can forbid ":" on the parsing
(defun socket-directory-character-p (char)
(or (member char #.(quote (coerce "/.-_" 'list)))
(alphanumericp char)))
(defrule dsn-hostname (and hostname (? dsn-port))
(defrule socket-directory (and "unix:" (* (socket-directory-character-p character)))
(:destructure (unix socket-directory)
(declare (ignore unix))
(list :unix (when socket-directory (text socket-directory)))))
(defrule network-name (and namestring (? (and "." network-name)))
(:lambda (name)
(list :host (text name))))
(defrule hostname (or ipv4 socket-directory network-name)
(:identity t))
(defrule dsn-hostname (and (? hostname) (? dsn-port))
(:destructure (hostname &optional port)
(append (list :host hostname) port)))
@ -303,9 +318,16 @@
(list :type type
:user user
:password password
:host (or host
:host (or (when host
(destructuring-bind (type &optional name) host
(ecase type
(:unix (if name (cons :unix name) :unix))
(:ipv4 name)
(:host name))))
(case type
(:postgresql (getenv-default "PGHOST" "localhost"))
(:postgresql (getenv-default "PGHOST"
#+unix :unix
#-unix "localhost"))
(:mysql (getenv-default "MYSQL_HOST" "localhost"))))
:port (or port
(parse-integer
@ -1919,7 +1941,7 @@ load database
(*myconn-pass* ,password))
,@body))
(:postgresql
`(let* ((*pgconn-host* ,host)
`(let* ((*pgconn-host* ,(if (consp host) (list 'quote host) host))
(*pgconn-port* ,port)
(*pgconn-user* ,user)
(*pgconn-pass* ,password))

View File

@ -31,27 +31,38 @@
(set-session-gucs *pg-settings* :transaction t)
,@forms)))
;; no database given, create a new database connection
`(pomo:with-connection (get-connection-spec ,dbname)
(log-message :debug "CONNECT")
(set-session-gucs *pg-settings*)
(handling-pgsql-notices ()
(pomo:with-transaction ()
(log-message :debug "BEGIN")
,@forms)))))
`(let ((cl-postgres:*unix-socket-dir*
(if (and (consp *pgconn-host*) (eq :unix (car *pgconn-host*)))
(fad:pathname-as-directory (cdr *pgconn-host*))
cl-postgres:*unix-socket-dir*)))
(pomo:with-connection (get-connection-spec ,dbname)
(log-message :debug "CONNECT")
(set-session-gucs *pg-settings*)
(handling-pgsql-notices ()
(pomo:with-transaction ()
(log-message :debug "BEGIN")
,@forms))))))
(defmacro with-pgsql-connection ((dbname) &body forms)
"Run FROMS within a PostgreSQL connection to DBNAME. To get the connection
spec from the DBNAME, use `get-connection-spec'."
`(pomo:with-connection (get-connection-spec ,dbname)
(log-message :debug "CONNECT")
(set-session-gucs *pg-settings*)
(handling-pgsql-notices ()
,@forms)))
`(let ((cl-postgres:*unix-socket-dir*
(if (and (consp *pgconn-host*) (eq :unix (car *pgconn-host*)))
(fad:pathname-as-directory (cdr *pgconn-host*))
cl-postgres:*unix-socket-dir*)))
(pomo:with-connection (get-connection-spec ,dbname)
(log-message :debug "CONNECT ~s" (get-connection-spec ,dbname))
(set-session-gucs *pg-settings*)
(handling-pgsql-notices ()
,@forms))))
(defun get-connection-spec (dbname &key (with-port t))
"pomo:with-connection and cl-postgres:open-database and open-db-writer are
not using the same connection spec format..."
(let ((conspec (list dbname *pgconn-user* *pgconn-pass* *pgconn-host*)))
(let* ((host (if (and (consp *pgconn-host*) (eq :unix (car *pgconn-host*)))
:unix
*pgconn-host*))
(conspec (list dbname *pgconn-user* *pgconn-pass* host)))
(if with-port
(append conspec (list :port *pgconn-port*))
(append conspec (list *pgconn-port*)))))
@ -100,19 +111,17 @@
(defun list-databases (&optional (username "postgres"))
"Connect to a local database and get the database list"
(pomo:with-connection (let ((*pgconn-user* username))
(get-connection-spec "postgres"))
(loop for (dbname) in (pomo:query
"select datname
(let* ((*pgconn-user* username))
(with-pgsql-transaction ("postgres")
(loop for (dbname) in (pomo:query
"select datname
from pg_database
where datname !~ 'postgres|template'")
collect dbname)))
collect dbname))))
(defun list-tables (dbname)
"Return an alist of tables names and list of columns to pay attention to."
(pomo:with-connection
(get-connection-spec dbname)
(with-pgsql-transaction (dbname)
(loop for (relname colarray) in (pomo:query "
select relname, array_agg(case when typname in ('date', 'timestamptz')
then attnum end
@ -133,11 +142,9 @@ select relname, array_agg(case when typname in ('date', 'timestamptz')
(defun list-tables-cols (dbname &key (schema "public") table-name-list)
"Return an alist of tables names and number of columns."
(pomo:with-connection
(get-connection-spec dbname)
(loop for (relname cols)
in (pomo:query (format nil "
(with-pgsql-transaction (dbname)
(loop for (relname cols)
in (pomo:query (format nil "
select relname, count(attnum)
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
@ -149,7 +156,7 @@ select relname, array_agg(case when typname in ('date', 'timestamptz')
~@[~{and relname = '~a'~^ ~}~]
group by relname
" schema table-name-list))
collect (cons relname cols))))
collect (cons relname cols))))
(defun list-tables-and-fkeys (&optional schema)
"Yet another table listing query."
@ -164,9 +171,7 @@ select relname, array_agg(case when typname in ('date', 'timestamptz')
(defun list-columns (dbname table-name &key schema)
"Return a list of column names for given TABLE-NAME."
(pomo:with-connection
(get-connection-spec dbname)
(with-pgsql-transaction (dbname)
(pomo:query (format nil "
select attname
from pg_class c
@ -183,7 +188,7 @@ select relname, array_agg(case when typname in ('date', 'timestamptz')
(defun reset-all-sequences (dbname &key tables)
"Reset all sequences to the max value of the column they are attached to."
(pomo:with-connection (get-connection-spec dbname)
(with-pgsql-connection (dbname)
(set-session-gucs *pg-settings*)
(pomo:execute "set client_min_messages to warning;")
(pomo:execute "listen seqs")

View File

@ -1,6 +1,6 @@
load database
from mysql://root@localhost/sakila
into postgresql://localhost:54393/sakila
into postgresql:///sakila
WITH include drop, create tables, no truncate,
create indexes, reset sequences, foreign keys