branch: elpa/pg commit 9e384860467ce9881bb015318138ec5929e7bc43 Author: Eric Marsden <eric.mars...@risk-engineering.org> Commit: Eric Marsden <eric.mars...@risk-engineering.org>
Use the 64-bit backend functions for large object functionality --- pg-lo.el | 100 +++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 35 deletions(-) diff --git a/pg-lo.el b/pg-lo.el index ec5d4f5bea..958bbd11c5 100644 --- a/pg-lo.el +++ b/pg-lo.el @@ -17,7 +17,9 @@ ;; For example, the user can define a new type called "circle", and define a C ;; or Tcl function called `circumference' which will act on circles. There is ;; also an inheritance mechanism in PostgreSQL. - +;; +;; https://www.postgresql.org/docs/current/lo-interfaces.html +;; https://www.postgresql.org/docs/current/lo-funcs.html ;;; Code: @@ -38,7 +40,6 @@ (declare-function pg-exec "pg" (con &rest args)) - (defvar pg-lo-initialized nil) (defvar pg-lo-functions '()) @@ -51,6 +52,7 @@ "proname = 'lo_unlink' OR " "proname = 'lo_lseek' OR " "proname = 'lo_tell' OR " + "proname = 'lo_truncate' OR " "proname = 'loread' OR " "proname = 'lowrite'"))) (setq pg-lo-functions '()) @@ -121,7 +123,7 @@ ;; FunctionCallResult (?V - (let ((msg-len (pg-read-net-int con 4)) + (let ((_msg-len (pg-read-net-int con 4)) (value-len (pg-read-net-int con 4))) (setq result (if integer-result (pg-read-net-int con value-len) @@ -174,8 +176,7 @@ ignored in PostgreSQL releases after v8.1." (error "Can't create large object")) (t oid)))) -;; mode = modestring (default "r", or "w" or "rw") -(defun pg-lo-open (connection oid &optional mode) +(defun pg-lo-open (con oid &optional mode) "Open the PostgreSQL large object designated by OID for reading. Uses PostgreSQL connection CON. The string MODE determines whether the object is opened for reading (`r'), or writing (`w'), or both (`rw'). @@ -189,47 +190,76 @@ Returns a large object descriptor that can be used with functions (logior pg--INV_READ pg--INV_WRITE)) (t (let ((msg (format "pg-lo-open: bad mode %s" mode))) (signal 'pg-user-error (list msg)))))) - (fd (pg-fn connection "lo_open" t oid mode))) + (fd (pg-fn con "lo_open" t oid mode))) (unless (integerp fd) (error "Couldn't open large object")) fd)) -;; TODO: should we be checking the return value and signalling a pg-error on failure? -(defsubst pg-lo-close (connection fd) - (pg-fn connection "lo_close" t fd)) +(defsubst pg-lo-close (con fd) + "Closes the PostgreSQL large object designated by FD. +Uses PostgreSQL connection CON." + (pg-fn con "lo_close" t fd)) -(defun pg-lo-read (connection fd bytes) - (let* ((encoded (pg-fn connection "loread" nil fd bytes)) +(defun pg-lo-read (con fd bytes) + "Read BYTES octets from large object designated by FD. +Uses PostgreSQL connection CON." + (let* ((encoded (pg-fn con "loread" nil fd bytes)) (hexdigits (substring encoded 2))) - ;; (message "lo-read: hex encoded is %s" encoded) (unless (and (eql 92 (aref encoded 0)) ; \ character (eql ?x (aref encoded 1))) (signal 'pg-protocol-error (list "Unexpected format for BYTEA binary string"))) (decode-hex-string hexdigits))) -(defsubst pg-lo-write (connection fd buf) - (pg-fn connection "lowrite" t fd buf)) - - -(defconst pg--SEEK_SET 0) -(defconst pg--SEEK_CUR 1) -(defconst pg--SEEK_END 2) - - -;; Whence can be SEEK_SET (seek from object start), SEEK_CUR (seek from current -;; position), and SEEK_END (seek from object end). -(defsubst pg-lo-lseek (connection fd offset whence) - (pg-fn connection "lo_lseek" t fd offset whence)) - -(defsubst pg-lo-tell (connection oid) - (pg-fn connection "lo_tell" t oid)) - -(defsubst pg-lo-truncate (con oid len) - (pg-fn con "lo_truncate" oid len)) - -(defsubst pg-lo-unlink (connection oid) - (pg-fn connection "lo_unlink" t oid)) +(defsubst pg-lo-write (con fd buf) + "Write the contents of BUF to the large object designated by FD. +Uses PostgreSQL connection CON." + (pg-fn con "lowrite" t fd buf)) + + +(defconst pg-SEEK_SET 0) +(defconst pg-SEEK_CUR 1) +(defconst pg-SEEK_END 2) + +(defun pg-lo-lseek (con fd offset whence) + "Seek to position OFFSET in PostgreSQL large object designated by FD. +WHENCE can be `pg-SEEK_SET' (seek from object start), +`pg-SEEK_CUR' (seek from current position), or `pg-SEEK_END' (seek from +object end). OFFSET may be a large integer (int8 type in PostgreSQL; +this function calls the PostgreSQL backend function `lo_lseek64'). Uses +PostgreSQL connection CON." + (let* ((res (pg-exec-prepared con "SELECT lo_lseek64($1, $2, $3)" + `((,fd . "int4") (,offset . "int8") (,whence . "int4")))) + (ret (cl-first (pg-result res :tuple 0)))) + (if (eql -1 ret) + (signal 'pg-operational-error (list "lo_lseek64 function call failed")) + ret))) + +(defun pg-lo-tell (con fd) + "Return the current file position in PostgreSQL large object FD. +Uses PostgreSQL connection CON. Uses the PostgreSQL backend function +`lo_tell64' to work with large objects." + (let* ((res (pg-exec-prepared con "SELECT lo_tell64($1)" `((,fd . "int4")))) + (ret (cl-first (pg-result res :tuple 0)))) + (if (eql -1 ret) + (signal 'pg-operational-error (list "lo_tell64 function call failed")) + ret))) + +(defun pg-lo-truncate (con fd len) + "Truncate the PostgreSQL large object FD to size LEN. +LEN may be a large integer (int8 type in PostgreSQL); this calls the +PostgreSQL backend function `lo_truncate64'. Uses PostgreSQL connection +CON." + (let* ((res (pg-exec-prepared con "SELECT lo_truncate64($1, $2)" + `((,fd . "int4") (,len . "int8")))) + (ret (cl-first (pg-result res :tuple 0)))) + (unless (zerop ret) + (signal 'pg-operational-error (list "lo_truncate64 function call failed"))))) + +(defun pg-lo-unlink (con oid) + "Unlink the PostgreSQL large object identified by OID. +Uses PostgreSQL connection CON." + (pg-fn con "lo_unlink" t oid)) ;; FIXME should use unwind-protect here (defun pg-lo-import (con filename) @@ -249,7 +279,7 @@ Uses PostgreSQL connection CON. Returns the OID of the new object." oid)) (defun pg-lo-export (con oid filename) - "Export PostgreSQL large object desingated by OID to FILENAME. + "Export PostgreSQL large object identified by OID to FILENAME. Uses PostgreSQL connection CON." (let* ((buf (get-buffer-create (format " *pg-%d" oid))) (fdin (pg-lo-open con oid "r")))