Add postgresql wrapper module (:std/db/postgresql)

ober

ba9438b08462f05edf9616732e176a12dcf8942c

diff --git a/Makefile b/Makefile
index 987d0e5..479d061 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,9 @@
 SCHEME = scheme
 LIBDIRS = lib
 # External chez-* library paths for wrapper modules
-CHEZ_EXT_LIBDIRS = $(HOME)/mine/chez-https/src:$(HOME)/mine/chez-ssl/src:$(HOME)/mine/chez-zlib/src:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-yaml:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll/src:$(HOME)/mine/chez-inotify/src:$(HOME)/mine/chez-crypto/src:$(HOME)/mine/chez-sqlite/src
+CHEZ_EXT_LIBDIRS = $(HOME)/mine/chez-https/src:$(HOME)/mine/chez-ssl/src:$(HOME)/mine/chez-zlib/src:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-yaml:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll/src:$(HOME)/mine/chez-inotify/src:$(HOME)/mine/chez-crypto/src:$(HOME)/mine/chez-sqlite/src:$(HOME)/mine/chez-postgresql/src
 # Shared object paths for FFI-based chez-* libraries
-CHEZ_EXT_LDPATH = $(HOME)/mine/chez-ssl:$(HOME)/mine/chez-zlib:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll:$(HOME)/mine/chez-inotify:$(HOME)/mine/chez-crypto:$(HOME)/mine/chez-sqlite
+CHEZ_EXT_LDPATH = $(HOME)/mine/chez-ssl:$(HOME)/mine/chez-zlib:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll:$(HOME)/mine/chez-inotify:$(HOME)/mine/chez-crypto:$(HOME)/mine/chez-sqlite:$(HOME)/mine/chez-postgresql
 
 .PHONY: test test-reader test-core test-runtime test-stdlib test-ffi test-modules test-expanded test-wrappers clean
 
@@ -69,6 +69,9 @@ test-wrappers:
 	@LD_LIBRARY_PATH="$(CHEZ_EXT_LDPATH):$$LD_LIBRARY_PATH" \
 		$(SCHEME) --libdirs "$(LIBDIRS):$(CHEZ_EXT_LIBDIRS)" --script tests/test-wrapper-sqlite.ss 2>/dev/null \
 		|| echo "  sqlite: SKIP (requires chez_sqlite_shim.so)"
+	@LD_LIBRARY_PATH="$(CHEZ_EXT_LDPATH):$$LD_LIBRARY_PATH" \
+		$(SCHEME) --libdirs "$(LIBDIRS):$(CHEZ_EXT_LIBDIRS)" --script tests/test-wrapper-postgresql.ss 2>/dev/null \
+		|| echo "  postgresql: SKIP (requires chez_pg_shim.so)"
 
 test-all: test test-wrappers
 
diff --git a/lib/std/db/postgresql.sls b/lib/std/db/postgresql.sls
new file mode 100644
index 0000000..d6c0c5c
--- /dev/null
+++ b/lib/std/db/postgresql.sls
@@ -0,0 +1,24 @@
+#!chezscheme
+;;; :std/db/postgresql -- PostgreSQL database (wraps chez-postgresql)
+;;; Requires: chez_pg_shim.so, libpq.so.5
+
+(library (std db postgresql)
+  (export
+    ;; Core
+    pg-connect pg-finish pg-status pg-error-message
+    pg-exec pg-exec* pg-query pg-eval
+    ;; Result access
+    pg-result-status pg-result-error pg-clear
+    pg-ntuples pg-nfields pg-fname pg-columns
+    pg-getvalue pg-getlength pg-getisnull
+    pg-cmd-tuples pg-ftype
+    ;; Utilities
+    pg-escape-literal pg-escape-identifier
+    pg-server-version pg-socket
+    ;; Constants
+    CONNECTION_OK CONNECTION_BAD
+    PGRES_EMPTY_QUERY PGRES_COMMAND_OK PGRES_TUPLES_OK PGRES_FATAL_ERROR)
+
+  (import (chez-postgresql))
+
+  ) ;; end library
diff --git a/libraries.md b/libraries.md
index bcbd250..3ddfef2 100644
--- a/libraries.md
+++ b/libraries.md
@@ -1,18 +1,18 @@
 # Jerboa Library Gap Analysis
 
-Gerbil has ~438 `:std/*` modules. Jerboa currently implements 41. This document tracks coverage and what's needed to close the gap.
+Gerbil has ~438 `:std/*` modules. Jerboa currently implements 51. This document tracks coverage and what's needed to close the gap.
 
-## Current Coverage (41 modules)
+## Current Coverage (51 modules)
 
 | Category | Jerboa | Gerbil | Coverage |
 |----------|--------|--------|----------|
 | Core (sort, format, error, sugar, pregexp, logger, test) | 7 | ~15 | 47% |
 | text/* (json, csv, hex, base64, utf8, xml, yaml) | 7 | ~15 | 47% |
 | misc/* (string, list, alist, ports, channel, thread, process, queue, bytes, uuid, repr, completion) | 12 | ~38 | 32% |
-| os/* (path, env, signal, temporaries, fdio) | 5 | ~16 | 31% |
-| crypto/* (digest) | 1 | ~9 | 11% |
+| os/* (path, env, signal, temporaries, fdio, epoll, inotify) | 7 | ~16 | 44% |
+| crypto/* (digest, cipher, hmac, pkey, kdf, etc) | 6 | ~9 | 67% |
 | net/* (request, httpd, ssl) | 3 | ~40+ | 8% |
-| db/* (leveldb) | 1 | ~4 | 25% |
+| db/* (leveldb, sqlite, postgresql) | 3 | ~4 | 75% |
 | cli/* (getopt) | 1 | ~4 | 25% |
 | srfi/* (13, 19) | 2 | ~60+ | 3% |
 | compress/* (zlib) | 1 | 0 | N/A |
@@ -31,20 +31,15 @@ Gerbil has ~438 `:std/*` modules. Jerboa currently implements 41. This document 
 | chez-yaml | YAML parser | `(std text yaml)` |
 | chez-leveldb | LevelDB | `(std db leveldb)` |
 
-### Needed
+### Completed (New)
 
-| Library | Would Fill | System Dep |
-|---------|-----------|------------|
-| **chez-crypto** | `(std crypto cipher)`, `(std crypto hmac)`, `(std crypto pkey)`, `(std crypto bn)`, `(std crypto dh)`, `(std crypto kdf)`, `(std crypto etc)` | libcrypto (OpenSSL) |
-| **chez-sqlite** | `(std db sqlite)` | libsqlite3 |
-| **chez-postgresql** | `(std db postgresql)` | libpq |
-
-### Maybe (OS-level)
-
-| Library | Would Fill | System Dep |
-|---------|-----------|------------|
-| **chez-epoll** | `(std os epoll)` | Linux kernel (epoll_create, epoll_ctl, epoll_wait) |
-| **chez-inotify** | `(std os inotify)` | Linux kernel (inotify) |
+| Library | Wraps | Jerboa Modules | Status |
+|---------|-------|---------------|--------|
+| chez-epoll | Linux epoll | `(std os epoll)` | Done |
+| chez-inotify | Linux inotify | `(std os inotify)` | Done |
+| chez-crypto | OpenSSL EVP | `(std crypto cipher)`, `(std crypto hmac)`, `(std crypto pkey)`, `(std crypto kdf)`, `(std crypto etc)` | Done |
+| chez-sqlite | SQLite3 | `(std db sqlite)` | Done |
+| chez-postgresql | libpq | `(std db postgresql)` | Done |
 
 ## Pure Scheme Modules (No External Deps)
 
@@ -123,8 +118,8 @@ Gerbil has ~438 `:std/*` modules. Jerboa currently implements 41. This document 
 | `(std os error)` | OS error codes | TODO |
 | `(std os fd)` | File descriptor utilities | TODO |
 | `(std os socket)` | Raw socket operations | TODO |
-| `(std os epoll)` | Linux epoll | Needs chez-epoll |
-| `(std os inotify)` | Linux inotify | Needs chez-inotify |
+| `(std os epoll)` | Linux epoll | Done |
+| `(std os inotify)` | Linux inotify | Done |
 | `(std os kqueue)` | BSD kqueue | N/A (Linux only) |
 | `(std os signalfd)` | Linux signalfd | TODO |
 
diff --git a/tests/test-wrapper-postgresql.ss b/tests/test-wrapper-postgresql.ss
new file mode 100644
index 0000000..4c84686
--- /dev/null
+++ b/tests/test-wrapper-postgresql.ss
@@ -0,0 +1,31 @@
+#!chezscheme
+(import (chezscheme) (std db postgresql))
+
+(define pass 0)
+(define fail 0)
+(define-syntax chk
+  (syntax-rules (=>)
+    [(_ expr => expected)
+     (let ([r expr] [e expected])
+       (if (equal? r e)
+         (set! pass (+ pass 1))
+         (begin (set! fail (+ fail 1))
+                (display "FAIL: ") (write 'expr)
+                (display " => ") (write r)
+                (display " expected ") (write e) (newline))))]))
+
+;; Verify constants loaded
+(chk CONNECTION_OK => 0)
+(chk PGRES_COMMAND_OK => 1)
+(chk PGRES_TUPLES_OK => 2)
+
+;; Verify connection failure raises error
+(chk (guard (exn [#t #t])
+       (pg-connect "host=localhost port=1 dbname=x connect_timeout=1")
+       #f) => #t)
+
+(newline)
+(display "postgresql wrapper: ")
+(display pass) (display " passed, ")
+(display fail) (display " failed") (newline)
+(when (> fail 0) (exit 1))