;;;; -*- mode:emacs-lisp;coding:utf-8 -*-
;;;;**************************************************************************
;;;;FILE:               psql.el
;;;;LANGUAGE:           emacs lisp
;;;;SYSTEM:             POSIX
;;;;USER-INTERFACE:     NONE
;;;;DESCRIPTION
;;;;
;;;;    psql is an interactive wrapper around pg.el
;;;;
;;;;AUTHORS
;;;;    <PJB> Pascal J. Bourguignon <pjb@informatimago.com>
;;;;MODIFICATIONS
;;;;    2010-10-05 <PJB> Created.
;;;;BUGS
;;;;LEGAL
;;;;    GPL
;;;;
;;;;    Copyright Pascal J. Bourguignon 2010 - 2011
;;;;
;;;;    This program is free software; you can redistribute it and/or
;;;;    modify it under the terms of the GNU General Public License
;;;;    as published by the Free Software Foundation; either version
;;;;    2 of the License, or (at your option) any later version.
;;;;
;;;;    This program is distributed in the hope that it will be
;;;;    useful, but WITHOUT ANY WARRANTY; without even the implied
;;;;    warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;;;;    PURPOSE.  See the GNU General Public License for more details.
;;;;
;;;;    You should have received a copy of the GNU General Public
;;;;    License along with this program; if not, write to the Free
;;;;    Software Foundation, Inc., 59 Temple Place, Suite 330,
;;;;    Boston, MA 02111-1307 USA
;;;;**************************************************************************

(require 'pg)
(require 'cl)

;;; Some more Common Lisp functions (usually from pjb-cl, but I want
;;; this file to be stand alone).

(defmacro cl:handler-case (expression &rest clauses)
  "Common-Lisp
IMPLEMENTATION: The clause variable symbols are substituted by one single
                condition-case variable symbol.  This may cause problems
                if the same symbol is used as data or if it's a dynamic
                variable.
"
  (let* ((var (gensym))
         (neclause (assoc :NO-ERROR clauses))
         (nell     (cadr neclause))
         (nebody   (cddr neclause))
         (handlers (mapcar (lambda (clause)
                             (let ((typespec (car clause))
                                   (clausvar (cadr clause))
                                   (body     (cddr clause)))
                               (cons (if (and (consp typespec)
                                              (eq 'or (car typespec)))
                                         (cdr typespec)
                                         typespec)
                                     (if (null clausvar)
                                         body
                                         (subst  var (car clausvar) body)))))
                           (remove neclause clauses))))
    (if neclause
        `(condition-case ,var
             (multiple-value-bind ,nell ,expression ,@nebody)
           ,@handlers)
        `(condition-case ,var
             ,expression
           ,@handlers))))


(defun cl:character (x)
  "Common-Lisp: Returns the character denoted by the character designator."
  (etypecase x
    (integer x)
    (string (aref x 0))
    (symbol (aref (symbol-name x) 0))))


(defun cl:char= (x y) (= x y))


(defun cl:string (x)
  "Common-Lisp: returns a string described by x.
Specifically:
  * If x is a string, it is returned.
  * If x is a symbol, its name is returned.
  * If x is a character, then a string containing that one character is returned.
"
  (etypecase x
    (integer (string x))
    (symbol (symbol-name x))
    (string  x)))


(defun cl:string-trim (character-bag string-designator)
  "Common-Lisp:  returns a substring of string, with all characters in character-bag stripped off the beginning and end.
"
  (unless (sequencep character-bag)
    (signal 'type-error  "Expected a sequence for `character-bag'."))
  (let* ((string (cl:string string-designator))
         (margin (format "[%s]*" (regexp-quote
                                  (if (stringp character-bag)
                                      character-bag
                                      (map 'string 'identity character-bag)))))
         (trimer (format "\\`%s\\(\\(.\\|\n\\)*?\\)%s\\'" margin margin)))
    (replace-regexp-in-string  trimer "\\1" string)))


(defun* cl:make-string (size &key (initial-element ??) (element-type 'character))
  "Common-Lisp: MAKE-STRING takes a key :initial-element argument
"
  (declare (ignore element-type))
  (make-string size initial-element))

(defun cl:princ-to-string (object)
  (let ((*print-circle* nil))
   (with-output-to-string (princ object))))



;;;
;;; psql buffer local variables:
;;;

(defvar psql:*db* nil
  "Buffer local variable holding the psql database.")

(defvar psql:*history* nil
  "Buffer local variable holding the command history.")

(defvar psql:*input-start* nil
  "Buffer local variable holding the position of start of the user input.")


(defun psql:format-table (ll)
  (let* ((titles   (car ll))
         (lines    (cdr ll))
         (widths   (mapcar (lambda (line) (mapcar (function length) line)) lines))
         col-widths top-cols)
    (macrolet ((h-bars
                   () `(do ((col-widths col-widths (cdr col-widths)))
                           ((null col-widths))
                         (insert (cl:make-string (car col-widths)
                                               :initial-element (cl:character "-")))
                         (insert (if (null (cdr col-widths)) "\n" " ")))))
      ;; compute the column widths:
      (setq col-widths (copy-seq (car widths)))
      (dolist (lin-widths widths)
        (do ((col-widths col-widths (cdr col-widths))
             (lin-widths lin-widths (cdr lin-widths)))
            ((null col-widths))
          (setf (car col-widths) (max (car col-widths) (car lin-widths)))))
      ;; compute the top-lines columns:
      (setq top-cols
            (do ((col-widths col-widths (cdr col-widths))
                 (cur-col 0 (+ 1 cur-col (car col-widths)))
                 (top-cols '()))
                ((null col-widths) (nreverse top-cols))
              (push (+ (/ (car col-widths) 2) cur-col) top-cols)))
      ;; print the titles
      (do ((titles   (reverse titles)   (cdr titles))
           (top-cols (reverse top-cols) (cdr top-cols))
           (right    "" (format "%s|%s"
                                (if (endp (rest titles))
                                    0
                                    (cl:make-string (- (first top-cols)
                                                     (or (second top-cols) 0) 1)
                                                  :initial-element (cl:character " ")))
                                right))
           name col)
          ((null titles))
        (setq name (car titles))
        (setq col (car top-cols))
        (insert
         (if (<= (length name) col)
             (format "%s%s+%s\n"
                     name
                     (cl:make-string (- col (length name))
                                   :initial-element (cl:character "-"))
                     right)
             (format "%s%s\n%sV%s\n"
                     name
                     (subseq right (- (length name) col 1))
                     (cl:make-string col  :initial-element (cl:character " "))
                     right))))
      ;; print the lines
      (do ((lines lines (cdr lines))
           (i 0 (1+ i)))
          ((null lines))
        (when (zerop (mod i 5)) (h-bars))
        (do ((line     (car lines) (cdr line))
             (col-widths col-widths (cdr col-widths)))
            ((null line))
          (insert (format (format "%%%ds%s"
                                  (car col-widths)
                                  (if (null (cdr line)) "" " "))
                          (car line))))
        (insert "\n"))
      (h-bars))))



(defun psql:insert-prompt ()
  "Inserts a prompt at the end of the psql buffer."
  (end-of-buffer)
  (insert "\npsql> ")
  (let ((inhibit-read-only t))
    (add-text-properties (point-min) (1- (point-max))
                         '(read-only t)))
  (make-variable-buffer-local 'psql:*input-start*)
  (goto-char (setf psql:*input-start* (point-max))))


(defun psql:insert-error (control-string &rest arguments)
  (goto-char (point-max))
  (let ((start (point)))
    (insert "\nERROR: " (apply (function format) control-string arguments) "\n")
    (add-text-properties start (point)'(face comment)))
  (psql:insert-prompt))

(defmacro* cl:string-case (string-expression &body clauses)
  (let ((value (gensym)))
    `(let ((,value ,string-expression))
       (cond
         ,@(mapcar (lambda (clause)
                     (destructuring-bind (constants &rest body) clause
                       (if (member* constants '(t otherwise) :test (function eql))
                           `(t ,@body)
                           `((member* ,value ',(ensure-list constants)
                                      :test (function cl:string-equal))
                             ,@body))))
                   clauses)))))

(defun psql:insert-result (result)
  (goto-char (point-max))
  (let ((status (pg:result result :status)))
    (if (string= status "SELECT")
        (let ((rows '()))
          (dolist (row (pg:result result :tuples))
            (push (mapcar (function cl:princ-to-string) row) rows))
          (insert "\n")
          (unless (null rows)
            (psql:format-table (cons (mapcar (function first) (pg:result result :attributes)) (nreverse rows)))))
        (insert (format "\n%s\n" (pg:result result :status))))
    (psql:insert-prompt)))


;;;
;;; The history
;;;

(defstruct (psql:history (:constructor psql:make-history))
  head tail current)

(defun psql:history-current-entry (history)
  (if (eq  (psql:history-current history) :tail)
      nil
      (car (psql:history-current history))))

(defun psql:write-history   (history item)
  (cond
    ((null (psql:history-tail history))
     (setf (psql:history-head history) (setf (psql:history-tail history)  (list item))))
    ((equalp item (car (psql:history-tail history))))
    (t
     (setf (cdr (psql:history-tail history)) (list item)
           (psql:history-tail history)       (cdr (psql:history-tail history)))))
  (setf (psql:history-current history) :new)
  history)

(defun psql:history-previous (history)
  (cond
    ((null (psql:history-current history)))
    ((eq (psql:history-current history) :new)
     (setf (psql:history-current history) (psql:history-tail history)))
    ((eq (psql:history-current history) :tail)
     (setf (psql:history-current history) (psql:history-tail history))
     (psql:history-previous history))
    ((eq (psql:history-current history)  (psql:history-head history))
     (setf (psql:history-current history) nil))
    (t
     (loop
        for previous = (psql:history-head history) then (cdr previous)
        until (eq (cdr previous) (psql:history-current history))
        finally (progn
                  (setf (psql:history-current history) previous)))))
  (psql:history-current-entry history))


(defun psql:history-next (history)
  (cond
    ((null (psql:history-current history))
     (setf (psql:history-current history) (cdr (psql:history-head history)))
     (when (null (psql:history-current history))
       (setf (psql:history-current history) :tail)))
    ((member (psql:history-current history) '(:tail :new))
     nil)
    (t
     (setf (psql:history-current history) (cdr (psql:history-current history)))
     (when (null (psql:history-current history))
       (setf (psql:history-current history) :tail))))
  (psql:history-current-entry history))

;; (let ((h (psql:make-history)))
;;   (psql:write-history h 'one)
;;   (psql:write-history h 'two)
;;   (psql:write-history h 'three)
;;   (psql:history-previous h)
;;   (psql:history-previous h)
;;   (psql:write-history h 'four)
;;   (psql:history-previous h)
;;   (psql:history-previous h)
;;   (psql:history-next h)
;;   (psql:history-next h)
;;   (psql:history-next h)
;;   (psql:history-current-entry h))



;;;
;;; The commands:
;;;

(defun psql-buffer-name (user host port dbname)
  "Returns the name of the psql buffer."
   (format "*postgres://%s@%s:%s/%s*" user host port dbname))

(defvar psql:*databases* '() "Database history for completion.")
(defvar psql:*users*     '() "User history for completion.")
(defvar psql:*hosts*     '() "Host history for completion.")
(defvar psql:*ports*     '() "Port history for completion.")
(defvar psql:*codings*   '() "Coding history for completion.")

(defmacro* psql:history-completion (title history &key (test ''string=) (postprocessing ''identity))
  (let ((form `(completing-read
                ,title
                (mapcar (lambda (x) (cons x nil)) ,history)
                (lambda (object) (pushnew (funcall ,postprocessing object) ,history :test ,test) t)
                nil
                nil
                ',history)))
    (if (eq postprocessing ''identity)
        form
        `(funcall ,postprocessing ,form))))

                     (macroexpand
                      '(psql:history-completion "Encoding: " psql:*codings* :test (function eql)
                                               :postprocessing (function intern)))

(defun psql (dbname user &optional password host port coding)
  "Creates a psql buffer where the user can insert sql queries and get the results."
  ;; (interactive "sDatabase: \nsUser: \nsPassword: \nsHost: \nnPort: \nzEncoding: ")
  (interactive (list (psql:history-completion "Database: " psql:*databases*)
                     (psql:history-completion "User: "     psql:*users*)
                     (read-passwd "Password: ")
                     (psql:history-completion "Host: "     psql:*hosts*)
                     (psql:history-completion "Port: "     psql:*ports*   :test (function eql))
                     (psql:history-completion "Encoding: " psql:*codings* :test (function eql)
                                              :postprocessing (function intern))))
  (let ((password (or password ""))
        (host     (or host "localhost"))
        (port     (or port 5432))
        (coding   (or coding 'latin-1)))
    (let ((db (pg:connect dbname user password host port coding)))
      (switch-to-buffer (get-buffer-create (psql-buffer-name user host port dbname)))
      (local-set-key (kbd "RET") 'psql-eval-last-expression)
      (local-set-key (kbd "M-p") 'psql-previous-input)
      (local-set-key (kbd "M-n") 'psql-next-input)
      (local-set-key (kbd "C-a") 'psql-beginning-of-line)
      (make-variable-buffer-local 'psql:*db*)
      (setf psql:*db* db)
      (make-variable-buffer-local 'psql:*history*)
      (unless (and (boundp 'psql:*history*) (psql:history-p psql:*history*))
        (setf psql:*history* (psql:make-history)))
      (psql:insert-prompt))))



(defvar *psql-command-map*
  '()

  "An a-list mapping command names (symbols, without the backslash) to
functions (taking any number of arguments, that are parsed as string
from the rest of the line). Example:

\df

")

(defun psql-eval-last-expression ()
  "This command is usually bound to RET, to evaluate the previous SQL query."
  (interactive)
  (let ((sql (cl:string-trim " ;\n\t\v\f" (buffer-substring psql:*input-start* (point-max)))))
    (if (and (plusp (length sql))
             (cl:char= (cl:character "\\") (aref sql 0)))
        (psql:insert-error "Commands are not implemented yet.")
        (progn
          (psql:write-history psql:*history* sql)
          (cl:handler-case (psql:insert-result (pg:exec psql:*db* sql))
            (error (err) (psql:insert-error (cl:princ-to-string err))))))))


(defun psql-previous-input ()
  (interactive)
  (let ((previous (psql:history-previous psql:*history*)))
    (if previous
        (progn
          (delete-region psql:*input-start* (point-max))
          (goto-char (point-max))
          (insert previous))
        (beep))))

(defun psql-next-input ()
  (interactive)
  (let ((next (psql:history-next psql:*history*)))
    (if next
        (progn
          (delete-region psql:*input-start* (point-max))
          (goto-char (point-max))
          (insert next))
        (beep))))

(defun psql-bol-point (&optional point)
  "Return the point of the beginning of line (current line, of line at `point')."
  (save-excursion
    (when point (goto-char point))
    (beginning-of-line)
    (point)))

(defun psql-eol-point (&optional point)
  "Return the point of the end of line (current line, of line at `point')."
  (save-excursion
    (when point (goto-char point))
    (end-of-line)
    (point)))


(defun psql-beginning-of-line ()
  (interactive)
  (if (and (<= (psql-bol-point psql:*input-start*) (point))
           (<= (point) (psql-eol-point psql:*input-start*)))
      (goto-char psql:*input-start*)
      (beginning-of-line)))

(defun psql-kill-ring-save-history ()
  "Put the whole psql command history into the kill ring."
  (interactive)
  (let ((history psql:*history*))
    (with-temp-buffer
      (dolist (item (psql:history-head history))
        (insert item ";\n"))
      (kill-ring-save (point-min) (point-max)))))



;;;
;;; Commands
;;;

(defmacro* psql:define-parameter (name default-value type docstring)
  ;; TODO: Gather all the information for help.
  `(progn
     (defvar ,name ,default-value ,docstring)
     (make-variable-buffer-local ',name)
     ',name))

(defmacro* psql:define-command (name parameters docstring &body body)
  `(progn
     (lambda ,parameters ,@body)
     ',name))



;; (defun psql:pset (option value)
;;   )
;; (defun psql:pget (option)
;;   )
;; (defsetf psql:pget (option) (value) `(psql:pset ,option ,value))

(psql:define-parameter psql:*print-aligned* t
  boolean "Whether the fields must be aligned in columns.")


(psql:define-command a ()
  "Toggle field alignment This makes little sense but we keep it around."
 (setf  psql:*print-aligned* (not psql:*print-aligned*)))


(psql:define-command C (title)
  "Override table title (formerly change HTML caption)."
  (setf psql:*table-title* title))


(psql:define-command c (dbname user host port)
  "
\\c or \\connect -- connect to database using the specified parameters.
*
\\c dbname user host port
*
If any of these parameters are omitted or specified as '-', the current
value of the parameter will be used instead. If the parameter has no
current value, the default value for that parameter will be used. Some
examples:
*
\\c - - hst             Connect to current database on current port of host
\"hst\" as current user. \\c - usr - prt   Connect to current database on
\"prt\" port of current host as user \"usr\". \\c dbs                     Connect to
\"dbs\" database on current port of current host as current user.
"
  ;; TODO: implement getting the default values, and the coding.
  (psql dbname user (read-passwd "Password: ") host port coding))


(psql:define-command cd (path)
  "Change the current directory."
  ;; TODO: Check the path.
  (setf default-directory path))

(psql:define-command copy ()
  "copy"
;;      /* \copy */
;;      else if (pg_strcasecmp(cmd, "copy") == 0)
;;      {
;;              /* Default fetch-it-all-and-print mode */
;;              instr_time      before,
;;                                      after;
;;
;;              char       *opt = psql_scan_slash_option(scan_state,
;;                                                                                               OT_WHOLE_LINE, NULL, false);
;;
;;              if (pset.timing)
;;                      INSTR_TIME_SET_CURRENT(before);
;;
;;              success = do_copy(opt);
;;
;;              if (pset.timing && success)
;;              {
;;                      INSTR_TIME_SET_CURRENT(after);
;;                      INSTR_TIME_SUBTRACT(after, before);
;;                      printf(_("Time: %.3f ms\n"), INSTR_TIME_GET_MILLISEC(after));
;;              }
;;
;;              free(opt);
;;      }

  )


(psql:define-command copyright ()
  "Prints the copyright"
  ;: TODO:
  )


;;      /* \d* commands */
;;      else if (cmd[0] == 'd')
;;      {
;;              char       *pattern;
;;              bool            show_verbose,
;;                                      show_system;
;;
;;              /* We don't do SQLID reduction on the pattern yet */
;;              pattern = psql_scan_slash_option(scan_state,
;;                                                                               OT_NORMAL, NULL, true);
;;
;;              show_verbose = strchr(cmd, '+') ? true : false;
;;              show_system = strchr(cmd, 'S') ? true : false;
;;
;;              switch (cmd[1])
;;              {
;;                      case '\0':
;;                      case '+':
;;                      case 'S':
;;                              if (pattern)
;;                                      success = describeTableDetails(pattern, show_verbose, show_system);
;;                              else
;;                                      /* standard listing of interesting things */
;;                                      success = listTables("tvs", NULL, show_verbose, show_system);
;;                              break;
;;                      case 'a':
;;                              success = describeAggregates(pattern, show_verbose, show_system);
;;                              break;
;;                      case 'b':
;;                              success = describeTablespaces(pattern, show_verbose);
;;                              break;
;;                      case 'c':
;;                              success = listConversions(pattern, show_system);
;;                              break;
;;                      case 'C':
;;                              success = listCasts(pattern);
;;                              break;
;;                      case 'd':
;;                              success = objectDescription(pattern, show_system);
;;                              break;
;;                      case 'D':
;;                              success = listDomains(pattern, show_system);
;;                              break;
;;                      case 'f':                       /* function subsystem */
;;                              switch (cmd[2])
;;                              {
;;                                      case '\0':
;;                                      case '+':
;;                                      case 'S':
;;                                      case 'a':
;;                                      case 'n':
;;                                      case 't':
;;                                      case 'w':
;;                                              success = describeFunctions(&cmd[2], pattern, show_verbose, show_system);
;;                                              break;
;;                                      default:
;;                                              status = PSQL_CMD_UNKNOWN;
;;                                              break;
;;                              }
;;                              break;
;;                      case 'g':
;;                              /* no longer distinct from \du */
;;                              success = describeRoles(pattern, show_verbose);
;;                              break;
;;                      case 'l':
;;                              success = do_lo_list();
;;                              break;
;;                      case 'n':
;;                              success = listSchemas(pattern, show_verbose);
;;                              break;
;;                      case 'o':
;;                              success = describeOperators(pattern, show_system);
;;                              break;
;;                      case 'p':
;;                              success = permissionsList(pattern);
;;                              break;
;;                      case 'T':
;;                              success = describeTypes(pattern, show_verbose, show_system);
;;                              break;
;;                      case 't':
;;                      case 'v':
;;                      case 'i':
;;                      case 's':
;;                              success = listTables(&cmd[1], pattern, show_verbose, show_system);
;;                              break;
;;                      case 'u':
;;                              success = describeRoles(pattern, show_verbose);
;;                              break;
;;                      case 'F':                       /* text search subsystem */
;;                              switch (cmd[2])
;;                              {
;;                                      case '\0':
;;                                      case '+':
;;                                              success = listTSConfigs(pattern, show_verbose);
;;                                              break;
;;                                      case 'p':
;;                                              success = listTSParsers(pattern, show_verbose);
;;                                              break;
;;                                      case 'd':
;;                                              success = listTSDictionaries(pattern, show_verbose);
;;                                              break;
;;                                      case 't':
;;                                              success = listTSTemplates(pattern, show_verbose);
;;                                              break;
;;                                      default:
;;                                              status = PSQL_CMD_UNKNOWN;
;;                                              break;
;;                              }
;;                              break;
;;                      case 'e':                       /* SQL/MED subsystem */
;;                              switch (cmd[2])
;;                              {
;;                                      case 's':
;;                                              success = listForeignServers(pattern, show_verbose);
;;                                              break;
;;                                      case 'u':
;;                                              success = listUserMappings(pattern, show_verbose);
;;                                              break;
;;                                      case 'w':
;;                                              success = listForeignDataWrappers(pattern, show_verbose);
;;                                              break;
;;                                      default:
;;                                              status = PSQL_CMD_UNKNOWN;
;;                                              break;
;;                              }
;;                              break;
;;                      default:
;;                              status = PSQL_CMD_UNKNOWN;
;;              }
;;
;;              if (pattern)
;;                      free(pattern);
;;      }
;;
;;
;;      /*
;;       * \e or \edit -- edit the current query buffer (or a file and make it the
;;       * query buffer
;;       */
;;      else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0)
;;      {
;;              if (!query_buf)
;;              {
;;                      psql_error("no query buffer\n");
;;                      status = PSQL_CMD_ERROR;
;;              }
;;              else
;;              {
;;                      char       *fname;
;;
;;                      fname = psql_scan_slash_option(scan_state,
;;                                                                                 OT_NORMAL, NULL, true);
;;                      expand_tilde(&fname);
;;                      if (fname)
;;                              canonicalize_path(fname);
;;                      if (do_edit(fname, query_buf, NULL))
;;                              status = PSQL_CMD_NEWEDIT;
;;                      else
;;                              status = PSQL_CMD_ERROR;
;;                      free(fname);
;;              }
;;      }
;;
;;      /*
;;       * \ef -- edit the named function, or present a blank CREATE FUNCTION
;;       * template if no argument is given
;;       */
;;      else if (strcmp(cmd, "ef") == 0)
;;      {
;;              if (!query_buf)
;;              {
;;                      psql_error("no query buffer\n");
;;                      status = PSQL_CMD_ERROR;
;;              }
;;              else
;;              {
;;                      char       *func;
;;                      Oid                     foid = InvalidOid;
;;
;;                      func = psql_scan_slash_option(scan_state,
;;                                                                                OT_WHOLE_LINE, NULL, true);
;;                      if (!func)
;;                      {
;;                              /* set up an empty command to fill in */
;;                              printfPQExpBuffer(query_buf,
;;                                                                "CREATE FUNCTION ( )\n"
;;                                                                " RETURNS \n"
;;                                                                " LANGUAGE \n"
;;                                                                " -- common options:  IMMUTABLE  STABLE  STRICT  SECURITY DEFINER\n"
;;                                                                "AS $function$\n"
;;                                                                "\n$function$\n");
;;                      }
;;                      else if (!lookup_function_oid(pset.db, func, &foid))
;;                      {
;;                              /* error already reported */
;;                              status = PSQL_CMD_ERROR;
;;                      }
;;                      else if (!get_create_function_cmd(pset.db, foid, query_buf))
;;                      {
;;                              /* error already reported */
;;                              status = PSQL_CMD_ERROR;
;;                      }
;;                      if (func)
;;                              free(func);
;;              }
;;
;;              if (status != PSQL_CMD_ERROR)
;;              {
;;                      bool            edited = false;
;;
;;                      if (!do_edit(0, query_buf, &edited))
;;                              status = PSQL_CMD_ERROR;
;;                      else if (!edited)
;;                              puts(_("No changes"));
;;                      else
;;                              status = PSQL_CMD_NEWEDIT;
;;              }
;;      }
;;
;;      /* \echo and \qecho */
;;      else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0)
;;      {
;;              char       *value;
;;              char            quoted;
;;              bool            no_newline = false;
;;              bool            first = true;
;;              FILE       *fout;
;;
;;              if (strcmp(cmd, "qecho") == 0)
;;                      fout = pset.queryFout;
;;              else
;;                      fout = stdout;
;;
;;              while ((value = psql_scan_slash_option(scan_state,
;;                                                                                         OT_NORMAL, &quoted, false)))
;;              {
;;                      if (!quoted && strcmp(value, "-n") == 0)
;;                              no_newline = true;
;;                      else
;;                      {
;;                              if (first)
;;                                      first = false;
;;                              else
;;                                      fputc(' ', fout);
;;                              fputs(value, fout);
;;                      }
;;                      free(value);
;;              }
;;              if (!no_newline)
;;                      fputs("\n", fout);
;;      }
;;
;;      /* \encoding -- set/show client side encoding */
;;      else if (strcmp(cmd, "encoding") == 0)
;;      {
;;              char       *encoding = psql_scan_slash_option(scan_state,
;;                                                                                                        OT_NORMAL, NULL, false);
;;
;;              if (!encoding)
;;              {
;;                      /* show encoding */
;;                      puts(pg_encoding_to_char(pset.encoding));
;;              }
;;              else
;;              {
;;                      /* set encoding */
;;                      if (PQsetClientEncoding(pset.db, encoding) == -1)
;;                              psql_error("%s: invalid encoding name or conversion procedure not found\n", encoding);
;;                      else
;;                      {
;;                              /* save encoding info into psql internal data */
;;                              pset.encoding = PQclientEncoding(pset.db);
;;                              pset.popt.topt.encoding = pset.encoding;
;;                              SetVariable(pset.vars, "ENCODING",
;;                                                      pg_encoding_to_char(pset.encoding));
;;                      }
;;                      free(encoding);
;;              }
;;      }
;;
;;      /* \f -- change field separator */
;;      else if (strcmp(cmd, "f") == 0)
;;      {
;;              char       *fname = psql_scan_slash_option(scan_state,
;;                                                                                                 OT_NORMAL, NULL, false);
;;
;;              success = do_pset("fieldsep", fname, &pset.popt, pset.quiet);
;;              free(fname);
;;      }
;;
;;      /* \g means send query */
;;      else if (strcmp(cmd, "g") == 0)
;;      {
;;              char       *fname = psql_scan_slash_option(scan_state,
;;                                                                                                 OT_FILEPIPE, NULL, false);
;;
;;              if (!fname)
;;                      pset.gfname = NULL;
;;              else
;;              {
;;                      expand_tilde(&fname);
;;                      pset.gfname = pg_strdup(fname);
;;              }
;;              free(fname);
;;              status = PSQL_CMD_SEND;
;;      }
;;
;;      /* help */
;;      else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
;;      {
;;              char       *opt = psql_scan_slash_option(scan_state,
;;                                                                                               OT_WHOLE_LINE, NULL, false);
;;
;;              helpSQL(opt, pset.popt.topt.pager);
;;              free(opt);
;;      }
;;
;;      /* HTML mode */
;;      else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
;;      {
;;              if (pset.popt.topt.format != PRINT_HTML)
;;                      success = do_pset("format", "html", &pset.popt, pset.quiet);
;;              else
;;                      success = do_pset("format", "aligned", &pset.popt, pset.quiet);
;;      }
;;
;;
;;      /* \i is include file */
;;      else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
;;      {
;;              char       *fname = psql_scan_slash_option(scan_state,
;;                                                                                                 OT_NORMAL, NULL, true);
;;
;;              if (!fname)
;;              {
;;                      psql_error("\\%s: missing required argument\n", cmd);
;;                      success = false;
;;              }
;;              else
;;              {
;;                      expand_tilde(&fname);
;;                      success = (process_file(fname, false) == EXIT_SUCCESS);
;;                      free(fname);
;;              }
;;      }
;;
;;      /* \l is list databases */
;;      else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0)
;;              success = listAllDbs(false);
;;      else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0)
;;              success = listAllDbs(true);
;;
;;      /*
;;       * large object things
;;       */
;;      else if (strncmp(cmd, "lo_", 3) == 0)
;;      {
;;              char       *opt1,
;;                                 *opt2;
;;
;;              opt1 = psql_scan_slash_option(scan_state,
;;                                                                        OT_NORMAL, NULL, true);
;;              opt2 = psql_scan_slash_option(scan_state,
;;                                                                        OT_NORMAL, NULL, true);
;;
;;              if (strcmp(cmd + 3, "export") == 0)
;;              {
;;                      if (!opt2)
;;                      {
;;                              psql_error("\\%s: missing required argument\n", cmd);
;;                              success = false;
;;                      }
;;                      else
;;                      {
;;                              expand_tilde(&opt2);
;;                              success = do_lo_export(opt1, opt2);
;;                      }
;;              }
;;
;;              else if (strcmp(cmd + 3, "import") == 0)
;;              {
;;                      if (!opt1)
;;                      {
;;                              psql_error("\\%s: missing required argument\n", cmd);
;;                              success = false;
;;                      }
;;                      else
;;                      {
;;                              expand_tilde(&opt1);
;;                              success = do_lo_import(opt1, opt2);
;;                      }
;;              }
;;
;;              else if (strcmp(cmd + 3, "list") == 0)
;;                      success = do_lo_list();
;;
;;              else if (strcmp(cmd + 3, "unlink") == 0)
;;              {
;;                      if (!opt1)
;;                      {
;;                              psql_error("\\%s: missing required argument\n", cmd);
;;                              success = false;
;;                      }
;;                      else
;;                              success = do_lo_unlink(opt1);
;;              }
;;
;;              else
;;                      status = PSQL_CMD_UNKNOWN;
;;
;;              free(opt1);
;;              free(opt2);
;;      }
;;
;;
;;      /* \o -- set query output */
;;      else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
;;      {
;;              char       *fname = psql_scan_slash_option(scan_state,
;;                                                                                                 OT_FILEPIPE, NULL, true);
;;
;;              expand_tilde(&fname);
;;              success = setQFout(fname);
;;              free(fname);
;;      }
;;
;;      /* \p prints the current query buffer */
;;      else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0)
;;      {
;;              if (query_buf && query_buf->len > 0)
;;                      puts(query_buf->data);
;;              else if (!pset.quiet)
;;                      puts(_("Query buffer is empty."));
;;              fflush(stdout);
;;      }
;;
;;      /* \password -- set user password */
;;      else if (strcmp(cmd, "password") == 0)
;;      {
;;              char       *pw1;
;;              char       *pw2;
;;
;;              pw1 = simple_prompt("Enter new password: ", 100, false);
;;              pw2 = simple_prompt("Enter it again: ", 100, false);
;;
;;              if (strcmp(pw1, pw2) != 0)
;;              {
;;                      fprintf(stderr, _("Passwords didn't match.\n"));
;;                      success = false;
;;              }
;;              else
;;              {
;;                      char       *opt0 = psql_scan_slash_option(scan_state, OT_SQLID, NULL, true);
;;                      char       *user;
;;                      char       *encrypted_password;
;;
;;                      if (opt0)
;;                              user = opt0;
;;                      else
;;                              user = PQuser(pset.db);
;;
;;                      encrypted_password = PQencryptPassword(pw1, user);
;;
;;                      if (!encrypted_password)
;;                      {
;;                              fprintf(stderr, _("Password encryption failed.\n"));
;;                              success = false;
;;                      }
;;                      else
;;                      {
;;                              PQExpBufferData buf;
;;                              PGresult   *res;
;;
;;                              initPQExpBuffer(&buf);
;;                              printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD ",
;;                                                                fmtId(user));
;;                              appendStringLiteralConn(&buf, encrypted_password, pset.db);
;;                              res = PSQLexec(buf.data, false);
;;                              termPQExpBuffer(&buf);
;;                              if (!res)
;;                                      success = false;
;;                              else
;;                                      PQclear(res);
;;                              PQfreemem(encrypted_password);
;;                      }
;;              }
;;
;;              free(pw1);
;;              free(pw2);
;;      }
;;
;;      /* \prompt -- prompt and set variable */
;;      else if (strcmp(cmd, "prompt") == 0)
;;      {
;;              char       *opt,
;;                                 *prompt_text = NULL;
;;              char       *arg1,
;;                                 *arg2;
;;
;;              arg1 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
;;              arg2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
;;
;;              if (!arg1)
;;              {
;;                      psql_error("\\%s: missing required argument\n", cmd);
;;                      success = false;
;;              }
;;              else
;;              {
;;                      char       *result;
;;
;;                      if (arg2)
;;                      {
;;                              prompt_text = arg1;
;;                              opt = arg2;
;;                      }
;;                      else
;;                              opt = arg1;
;;
;;                      if (!pset.inputfile)
;;                              result = simple_prompt(prompt_text, 4096, true);
;;                      else
;;                      {
;;                              if (prompt_text)
;;                              {
;;                                      fputs(prompt_text, stdout);
;;                                      fflush(stdout);
;;                              }
;;                              result = gets_fromFile(stdin);
;;                      }
;;
;;                      if (!SetVariable(pset.vars, opt, result))
;;                      {
;;                              psql_error("\\%s: error\n", cmd);
;;                              success = false;
;;                      }
;;
;;                      free(result);
;;                      if (prompt_text)
;;                              free(prompt_text);
;;                      free(opt);
;;              }
;;      }
;;
;;      /* \pset -- set printing parameters */
;;      else if (strcmp(cmd, "pset") == 0)
;;      {
;;              char       *opt0 = psql_scan_slash_option(scan_state,
;;                                                                                                OT_NORMAL, NULL, false);
;;              char       *opt1 = psql_scan_slash_option(scan_state,
;;                                                                                                OT_NORMAL, NULL, false);
;;
;;              if (!opt0)
;;              {
;;                      psql_error("\\%s: missing required argument\n", cmd);
;;                      success = false;
;;              }
;;              else
;;                      success = do_pset(opt0, opt1, &pset.popt, pset.quiet);
;;
;;              free(opt0);
;;              free(opt1);
;;      }
;;
;;      /* \q or \quit */
;;      else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0)
;;              status = PSQL_CMD_TERMINATE;
;;
;;      /* reset(clear) the buffer */
;;      else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0)
;;      {
;;              resetPQExpBuffer(query_buf);
;;              psql_scan_reset(scan_state);
;;              if (!pset.quiet)
;;                      puts(_("Query buffer reset (cleared)."));
;;      }
;;
;;      /* \s save history in a file or show it on the screen */
;;      else if (strcmp(cmd, "s") == 0)
;;      {
;;              char       *fname = psql_scan_slash_option(scan_state,
;;                                                                                                 OT_NORMAL, NULL, true);
;;
;;              expand_tilde(&fname);
;;              /* This scrolls off the screen when using /dev/tty */
;;              success = saveHistory(fname ? fname : DEVTTY, false);
;;              if (success && !pset.quiet && fname)
;;                      printf(gettext("Wrote history to file \"%s/%s\".\n"),
;;                                 pset.dirname ? pset.dirname : ".", fname);
;;              if (!fname)
;;                      putchar('\n');
;;              free(fname);
;;      }
;;
;;      /* \set -- generalized set variable/option command */
;;      else if (strcmp(cmd, "set") == 0)
;;      {
;;              char       *opt0 = psql_scan_slash_option(scan_state,
;;                                                                                                OT_NORMAL, NULL, false);
;;
;;              if (!opt0)
;;              {
;;                      /* list all variables */
;;                      PrintVariables(pset.vars);
;;                      success = true;
;;              }
;;              else
;;              {
;;                      /*
;;                       * Set variable to the concatenation of the arguments.
;;                       */
;;                      char       *newval;
;;                      char       *opt;
;;
;;                      opt = psql_scan_slash_option(scan_state,
;;                                                                               OT_NORMAL, NULL, false);
;;                      newval = pg_strdup(opt ? opt : "");
;;                      free(opt);
;;
;;                      while ((opt = psql_scan_slash_option(scan_state,
;;                                                                                               OT_NORMAL, NULL, false)))
;;                      {
;;                              newval = realloc(newval, strlen(newval) + strlen(opt) + 1);
;;                              if (!newval)
;;                              {
;;                                      psql_error("out of memory\n");
;;                                      exit(EXIT_FAILURE);
;;                              }
;;                              strcat(newval, opt);
;;                              free(opt);
;;                      }
;;
;;                      if (!SetVariable(pset.vars, opt0, newval))
;;                      {
;;                              psql_error("\\%s: error\n", cmd);
;;                              success = false;
;;                      }
;;                      free(newval);
;;              }
;;              free(opt0);
;;      }
;;
;;      /* \t -- turn off headers and row count */
;;      else if (strcmp(cmd, "t") == 0)
;;      {
;;              char       *opt = psql_scan_slash_option(scan_state,
;;                                                                                               OT_NORMAL, NULL, true);
;;
;;              success = do_pset("tuples_only", opt, &pset.popt, pset.quiet);
;;              free(opt);
;;      }
;;
;;
;;      /* \T -- define html <table ...> attributes */
;;      else if (strcmp(cmd, "T") == 0)
;;      {
;;              char       *value = psql_scan_slash_option(scan_state,
;;                                                                                                 OT_NORMAL, NULL, false);
;;
;;              success = do_pset("tableattr", value, &pset.popt, pset.quiet);
;;              free(value);
;;      }
;;
;;      /* \timing -- toggle timing of queries */
;;      else if (strcmp(cmd, "timing") == 0)
;;      {
;;              char       *opt = psql_scan_slash_option(scan_state,
;;                                                                                               OT_NORMAL, NULL, false);
;;
;;              if (opt)
;;                      pset.timing = ParseVariableBool(opt);
;;              else
;;                      pset.timing = !pset.timing;
;;              if (!pset.quiet)
;;              {
;;                      if (pset.timing)
;;                              puts(_("Timing is on."));
;;                      else
;;                              puts(_("Timing is off."));
;;              }
;;              free(opt);
;;      }
;;
;;      /* \unset */
;;      else if (strcmp(cmd, "unset") == 0)
;;      {
;;              char       *opt = psql_scan_slash_option(scan_state,
;;                                                                                               OT_NORMAL, NULL, false);
;;
;;              if (!opt)
;;              {
;;                      psql_error("\\%s: missing required argument\n", cmd);
;;                      success = false;
;;              }
;;              else if (!SetVariable(pset.vars, opt, NULL))
;;              {
;;                      psql_error("\\%s: error\n", cmd);
;;                      success = false;
;;              }
;;              free(opt);
;;      }
;;
;;      /* \w -- write query buffer to file */
;;      else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0)
;;      {
;;              FILE       *fd = NULL;
;;              bool            is_pipe = false;
;;              char       *fname = NULL;
;;
;;              if (!query_buf)
;;              {
;;                      psql_error("no query buffer\n");
;;                      status = PSQL_CMD_ERROR;
;;              }
;;              else
;;              {
;;                      fname = psql_scan_slash_option(scan_state,
;;                                                                                 OT_FILEPIPE, NULL, true);
;;                      expand_tilde(&fname);
;;
;;                      if (!fname)
;;                      {
;;                              psql_error("\\%s: missing required argument\n", cmd);
;;                              success = false;
;;                      }
;;                      else
;;                      {
;;                              if (fname[0] == '|')
;;                              {
;;                                      is_pipe = true;
;;                                      fd = popen(&fname[1], "w");
;;                              }
;;                              else
;;                              {
;;                                      canonicalize_path(fname);
;;                                      fd = fopen(fname, "w");
;;                              }
;;                              if (!fd)
;;                              {
;;                                      psql_error("%s: %s\n", fname, strerror(errno));
;;                                      success = false;
;;                              }
;;                      }
;;              }
;;
;;              if (fd)
;;              {
;;                      int                     result;
;;
;;                      if (query_buf && query_buf->len > 0)
;;                              fprintf(fd, "%s\n", query_buf->data);
;;
;;                      if (is_pipe)
;;                              result = pclose(fd);
;;                      else
;;                              result = fclose(fd);
;;
;;                      if (result == EOF)
;;                      {
;;                              psql_error("%s: %s\n", fname, strerror(errno));
;;                              success = false;
;;                      }
;;              }
;;
;;              free(fname);
;;      }
;;
;;      /* \x -- toggle expanded table representation */
;;      else if (strcmp(cmd, "x") == 0)
;;      {
;;              char       *opt = psql_scan_slash_option(scan_state,
;;                                                                                               OT_NORMAL, NULL, true);
;;
;;              success = do_pset("expanded", opt, &pset.popt, pset.quiet);
;;              free(opt);
;;      }
;;
;;      /* \z -- list table rights (equivalent to \dp) */
;;      else if (strcmp(cmd, "z") == 0)
;;      {
;;              char       *pattern = psql_scan_slash_option(scan_state,
;;                                                                                                       OT_NORMAL, NULL, true);
;;
;;              success = permissionsList(pattern);
;;              if (pattern)
;;                      free(pattern);
;;      }
;;
;;      /* \! -- shell escape */
;;      else if (strcmp(cmd, "!") == 0)
;;      {
;;              char       *opt = psql_scan_slash_option(scan_state,
;;                                                                                               OT_WHOLE_LINE, NULL, false);
;;
;;              success = do_shell(opt);
;;              free(opt);
;;      }
;;
;;      /* \? -- slash command help */
;;      else if (strcmp(cmd, "?") == 0)
;;              slashUsage(pset.popt.topt.pager);
;;
;; #if 0
;;
;;      /*
;;       * These commands don't do anything. I just use them to test the parser.
;;       */
;;      else if (strcmp(cmd, "void") == 0 || strcmp(cmd, "#") == 0)
;;      {
;;              int                     i = 0;
;;              char       *value;
;;
;;              while ((value = psql_scan_slash_option(scan_state,
;;                                                                                         OT_NORMAL, NULL, true)))
;;              {
;;                      fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value);
;;                      free(value);
;;              }
;;      }
;; #endif
;;
;;      else
;;              status = PSQL_CMD_UNKNOWN;
;;
;;      if (!success)
;;              status = PSQL_CMD_ERROR;
;;
;;      return status;
;; }
;;
;;
;;
;; ;;;
;; ;;; Describe commands
;; ;;;
;;
;;
;; /*
;;  * psql - the PostgreSQL interactive terminal
;;  *
;;  * Support for the various \d ("describe") commands.  Note that the current
;;  * expectation is that all functions in this file will succeed when working
;;  * with servers of versions 7.4 and up.  It's okay to omit irrelevant
;;  * information for an old server, but not to fail outright.
;;  *
;;  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
;;  *
;;  * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.218.2.3 2010/03/01 20:55:53 heikki Exp $
;;  */
;; #include "postgres_fe.h"
;;
;; #include <ctype.h>
;;
;; #include "common.h"
;; #include "describe.h"
;; #include "dumputils.h"
;; #include "mbprint.h"
;; #include "print.h"
;; #include "settings.h"
;; #include "variables.h"
;;
;;
;; static bool describeOneTableDetails(const char *schemaname,
;;                                              const char *relationname,
;;                                              const char *oid,
;;                                              bool verbose);
;; static void add_tablespace_footer(printTableContent *const cont, char relkind,
;;                                        Oid tablespace, const bool newline);
;; static void add_role_attribute(PQExpBuffer buf, const char *const str);
;; static bool listTSParsersVerbose(const char *pattern);
;; static bool describeOneTSParser(const char *oid, const char *nspname,
;;                                      const char *prsname);
;; static bool listTSConfigsVerbose(const char *pattern);
;; static bool describeOneTSConfig(const char *oid, const char *nspname,
;;                                      const char *cfgname,
;;                                      const char *pnspname, const char *prsname);
;; static void printACLColumn(PQExpBuffer buf, const char *colname);
;;
;;
;; /*----------------
;;  * Handlers for various slash commands displaying some sort of list
;;  * of things in the database.
;;  *
;;  * Note: try to format the queries to look nice in -E output.
;;  *----------------
;;  */
;;
;;
;; /* \da
;;  * Takes an optional regexp to select particular aggregates
;;  */
;; bool
;; describeAggregates(const char *pattern, bool verbose, bool showSystem)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT n.nspname as \"%s\",\n"
;;                                        "  p.proname AS \"%s\",\n"
;;                               "  pg_catalog.format_type(p.prorettype, NULL) AS \"%s\",\n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Result data type"));
;;
;;      if (pset.sversion >= 80200)
;;              appendPQExpBuffer(&buf,
;;                                                "  CASE WHEN p.pronargs = 0\n"
;;                                                "    THEN CAST('*' AS pg_catalog.text)\n"
;;                                                "    ELSE\n"
;;                                                "    pg_catalog.array_to_string(ARRAY(\n"
;;                                                "      SELECT\n"
;;                               "        pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
;;                                                "      FROM\n"
;;                                                "        pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
;;                                                "    ), ', ')\n"
;;                                                "  END AS \"%s\",\n",
;;                                                gettext_noop("Argument data types"));
;;      else
;;              appendPQExpBuffer(&buf,
;;                       "  pg_catalog.format_type(p.proargtypes[0], NULL) AS \"%s\",\n",
;;                                                gettext_noop("Argument data types"));
;;
;;      appendPQExpBuffer(&buf,
;;                               "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
;;                                        "FROM pg_catalog.pg_proc p\n"
;;         "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
;;                                        "WHERE p.proisagg\n",
;;                                        gettext_noop("Description"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "p.proname", NULL,
;;                                                "pg_catalog.pg_function_is_visible(p.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2, 4;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of aggregate functions");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; /* \db
;;  * Takes an optional regexp to select particular tablespaces
;;  */
;; bool
;; describeTablespaces(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      if (pset.sversion < 80000)
;;      {
;;              fprintf(stderr, _("The server (version %d.%d) does not support tablespaces.\n"),
;;                              pset.sversion / 10000, (pset.sversion / 100) % 100);
;;              return true;
;;      }
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT spcname AS \"%s\",\n"
;;                                        "  pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n"
;;                                        "  spclocation AS \"%s\"",
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Owner"),
;;                                        gettext_noop("Location"));
;;
;;      if (verbose)
;;      {
;;              appendPQExpBuffer(&buf, ",\n  ");
;;              printACLColumn(&buf, "spcacl");
;;      }
;;
;;      if (verbose && pset.sversion >= 80200)
;;              appendPQExpBuffer(&buf,
;;               ",\n  pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
;;                                                gettext_noop("Description"));
;;
;;      appendPQExpBuffer(&buf,
;;                                        "\nFROM pg_catalog.pg_tablespace\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                NULL, "spcname", NULL,
;;                                                NULL);
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of tablespaces");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /* \df
;;  * Takes an optional regexp to select particular functions.
;;  *
;;  * As with \d, you can specify the kinds of functions you want:
;;  *
;;  * a for aggregates
;;  * n for normal
;;  * t for trigger
;;  * w for window
;;  *
;;  * and you can mix and match these in any order.
;;  */
;; bool
;; describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem)
;; {
;;      bool            showAggregate = strchr(functypes, 'a') != NULL;
;;      bool            showNormal = strchr(functypes, 'n') != NULL;
;;      bool            showTrigger = strchr(functypes, 't') != NULL;
;;      bool            showWindow = strchr(functypes, 'w') != NULL;
;;      bool            have_where;
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;      static const bool translate_columns[] = {false, false, false, false, true, true, false, false, false, false};
;;
;;      if (strlen(functypes) != strspn(functypes, "antwS+"))
;;      {
;;              fprintf(stderr, _("\\df only takes [antwS+] as options\n"));
;;              return true;
;;      }
;;
;;      if (showWindow && pset.sversion < 80400)
;;      {
;;              fprintf(stderr, _("\\df does not take a \"w\" option with server version %d.%d\n"),
;;                              pset.sversion / 10000, (pset.sversion / 100) % 100);
;;              return true;
;;      }
;;
;;      if (!showAggregate && !showNormal && !showTrigger && !showWindow)
;;      {
;;              showAggregate = showNormal = showTrigger = true;
;;              if (pset.sversion >= 80400)
;;                      showWindow = true;
;;      }
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT n.nspname as \"%s\",\n"
;;                                        "  p.proname as \"%s\",\n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"));
;;
;;      if (pset.sversion >= 80400)
;;              appendPQExpBuffer(&buf,
;;                                      "  pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
;;                               "  pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
;;                                                " CASE\n"
;;                                                "  WHEN p.proisagg THEN '%s'\n"
;;                                                "  WHEN p.proiswindow THEN '%s'\n"
;;                                                "  WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
;;                                                "  ELSE '%s'\n"
;;                                                "END as \"%s\"",
;;                                                gettext_noop("Result data type"),
;;                                                gettext_noop("Argument data types"),
;;              /* translator: "agg" is short for "aggregate" */
;;                                                gettext_noop("agg"),
;;                                                gettext_noop("window"),
;;                                                gettext_noop("trigger"),
;;                                                gettext_noop("normal"),
;;                                                gettext_noop("Type"));
;;      else if (pset.sversion >= 80100)
;;              appendPQExpBuffer(&buf,
;;                                       "  CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
;;                                "  pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
;;                                                "  CASE WHEN proallargtypes IS NOT NULL THEN\n"
;;                                                "    pg_catalog.array_to_string(ARRAY(\n"
;;                                                "      SELECT\n"
;;                                                "        CASE\n"
;;                                                "          WHEN p.proargmodes[s.i] = 'i' THEN ''\n"
;;                                        "          WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n"
;;                                      "          WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
;;                               "          WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n"
;;                                                "        END ||\n"
;;                                                "        CASE\n"
;;                       "          WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n"
;;                                                "          ELSE p.proargnames[s.i] || ' ' \n"
;;                                                "        END ||\n"
;;                        "        pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n"
;;                                                "      FROM\n"
;;                                                "        pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n"
;;                                                "    ), ', ')\n"
;;                                                "  ELSE\n"
;;                                                "    pg_catalog.array_to_string(ARRAY(\n"
;;                                                "      SELECT\n"
;;                                                "        CASE\n"
;;                 "          WHEN COALESCE(p.proargnames[s.i+1], '') = '' THEN ''\n"
;;                                                "          ELSE p.proargnames[s.i+1] || ' '\n"
;;                                                "          END ||\n"
;;                               "        pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
;;                                                "      FROM\n"
;;                                                "        pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
;;                                                "    ), ', ')\n"
;;                                                "  END AS \"%s\",\n"
;;                                                "  CASE\n"
;;                                                "    WHEN p.proisagg THEN '%s'\n"
;;                                                "    WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
;;                                                "    ELSE '%s'\n"
;;                                                "  END AS \"%s\"",
;;                                                gettext_noop("Result data type"),
;;                                                gettext_noop("Argument data types"),
;;              /* translator: "agg" is short for "aggregate" */
;;                                                gettext_noop("agg"),
;;                                                gettext_noop("trigger"),
;;                                                gettext_noop("normal"),
;;                                                gettext_noop("Type"));
;;      else
;;              appendPQExpBuffer(&buf,
;;                                       "  CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
;;                                "  pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
;;                                      "  pg_catalog.oidvectortypes(p.proargtypes) as \"%s\",\n"
;;                                                "  CASE\n"
;;                                                "    WHEN p.proisagg THEN '%s'\n"
;;                                                "    WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
;;                                                "    ELSE '%s'\n"
;;                                                "  END AS \"%s\"",
;;                                                gettext_noop("Result data type"),
;;                                                gettext_noop("Argument data types"),
;;              /* translator: "agg" is short for "aggregate" */
;;                                                gettext_noop("agg"),
;;                                                gettext_noop("trigger"),
;;                                                gettext_noop("normal"),
;;                                                gettext_noop("Type"));
;;
;;      if (verbose)
;;              appendPQExpBuffer(&buf,
;;                                                ",\n CASE\n"
;;                                                "  WHEN p.provolatile = 'i' THEN '%s'\n"
;;                                                "  WHEN p.provolatile = 's' THEN '%s'\n"
;;                                                "  WHEN p.provolatile = 'v' THEN '%s'\n"
;;                                                "END as \"%s\""
;;                                 ",\n  pg_catalog.pg_get_userbyid(p.proowner) as \"%s\",\n"
;;                                                "  l.lanname as \"%s\",\n"
;;                                                "  p.prosrc as \"%s\",\n"
;;                                "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"",
;;                                                gettext_noop("immutable"),
;;                                                gettext_noop("stable"),
;;                                                gettext_noop("volatile"),
;;                                                gettext_noop("Volatility"),
;;                                                gettext_noop("Owner"),
;;                                                gettext_noop("Language"),
;;                                                gettext_noop("Source code"),
;;                                                gettext_noop("Description"));
;;
;;      appendPQExpBuffer(&buf,
;;                                        "\nFROM pg_catalog.pg_proc p"
;;      "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n");
;;
;;      if (verbose)
;;              appendPQExpBuffer(&buf,
;;                 "     LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n");
;;
;;      have_where = false;
;;
;;      /* filter by function type, if requested */
;;      if (showNormal && showAggregate && showTrigger && showWindow)
;;               /* Do nothing */ ;
;;      else if (showNormal)
;;      {
;;              if (!showAggregate)
;;              {
;;                      if (have_where)
;;                              appendPQExpBuffer(&buf, "      AND ");
;;                      else
;;                      {
;;                              appendPQExpBuffer(&buf, "WHERE ");
;;                              have_where = true;
;;                      }
;;                      appendPQExpBuffer(&buf, "NOT p.proisagg\n");
;;              }
;;              if (!showTrigger)
;;              {
;;                      if (have_where)
;;                              appendPQExpBuffer(&buf, "      AND ");
;;                      else
;;                      {
;;                              appendPQExpBuffer(&buf, "WHERE ");
;;                              have_where = true;
;;                      }
;;                      appendPQExpBuffer(&buf, "p.prorettype <> 'pg_catalog.trigger'::pg_catalog.regtype\n");
;;              }
;;              if (!showWindow && pset.sversion >= 80400)
;;              {
;;                      if (have_where)
;;                              appendPQExpBuffer(&buf, "      AND ");
;;                      else
;;                      {
;;                              appendPQExpBuffer(&buf, "WHERE ");
;;                              have_where = true;
;;                      }
;;                      appendPQExpBuffer(&buf, "NOT p.proiswindow\n");
;;              }
;;      }
;;      else
;;      {
;;              bool            needs_or = false;
;;
;;              appendPQExpBuffer(&buf, "WHERE (\n       ");
;;              have_where = true;
;;              /* Note: at least one of these must be true ... */
;;              if (showAggregate)
;;              {
;;                      appendPQExpBuffer(&buf, "p.proisagg\n");
;;                      needs_or = true;
;;              }
;;              if (showTrigger)
;;              {
;;                      if (needs_or)
;;                              appendPQExpBuffer(&buf, "       OR ");
;;                      appendPQExpBuffer(&buf,
;;                              "p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype\n");
;;                      needs_or = true;
;;              }
;;              if (showWindow)
;;              {
;;                      if (needs_or)
;;                              appendPQExpBuffer(&buf, "       OR ");
;;                      appendPQExpBuffer(&buf, "p.proiswindow\n");
;;                      needs_or = true;
;;              }
;;              appendPQExpBuffer(&buf, "      )\n");
;;      }
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, have_where, false,
;;                                                "n.nspname", "p.proname", NULL,
;;                                                "pg_catalog.pg_function_is_visible(p.oid)");
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2, 4;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of functions");
;;      myopt.translate_header = true;
;;      myopt.translate_columns = translate_columns;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;;
;; /*
;;  * \dT
;;  * describe types
;;  */
;; bool
;; describeTypes(const char *pattern, bool verbose, bool showSystem)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT n.nspname as \"%s\",\n"
;;                                        "  pg_catalog.format_type(t.oid, NULL) AS \"%s\",\n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"));
;;      if (verbose)
;;              appendPQExpBuffer(&buf,
;;                                                "  t.typname AS \"%s\",\n"
;;                                                "  CASE WHEN t.typrelid != 0\n"
;;                                                "      THEN CAST('tuple' AS pg_catalog.text)\n"
;;                                                "    WHEN t.typlen < 0\n"
;;                                                "      THEN CAST('var' AS pg_catalog.text)\n"
;;                                                "    ELSE CAST(t.typlen AS pg_catalog.text)\n"
;;                                                "  END AS \"%s\",\n",
;;                                                gettext_noop("Internal name"),
;;                                                gettext_noop("Size"));
;;      if (verbose && pset.sversion >= 80300)
;;              appendPQExpBuffer(&buf,
;;                                                "  pg_catalog.array_to_string(\n"
;;                                                "      ARRAY(\n"
;;                                                "                  SELECT e.enumlabel\n"
;;                                                "          FROM pg_catalog.pg_enum e\n"
;;                                                "          WHERE e.enumtypid = t.oid\n"
;;                                                "          ORDER BY e.oid\n"
;;                                                "      ),\n"
;;                                                "      E'\\n'\n"
;;                                                "  ) AS \"%s\",\n",
;;                                                gettext_noop("Elements"));
;;
;;      appendPQExpBuffer(&buf,
;;                              "  pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
;;                                        gettext_noop("Description"));
;;
;;      appendPQExpBuffer(&buf, "FROM pg_catalog.pg_type t\n"
;;       "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
;;
;;      /*
;;       * do not include complex types (typrelid!=0) unless they are standalone
;;       * composite types
;;       */
;;      appendPQExpBuffer(&buf, "WHERE (t.typrelid = 0 ");
;;      appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c "
;;                                        "WHERE c.oid = t.typrelid))\n");
;;
;;      /*
;;       * do not include array types (before 8.3 we have to use the assumption
;;       * that their names start with underscore)
;;       */
;;      if (pset.sversion >= 80300)
;;              appendPQExpBuffer(&buf, "  AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)\n");
;;      else
;;              appendPQExpBuffer(&buf, "  AND t.typname !~ '^_'\n");
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      /* Match name pattern against either internal or external name */
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "t.typname",
;;                                                "pg_catalog.format_type(t.oid, NULL)",
;;                                                "pg_catalog.pg_type_is_visible(t.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of data types");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /* \do
;;  */
;; bool
;; describeOperators(const char *pattern, bool showSystem)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT n.nspname as \"%s\",\n"
;;                                        "  o.oprname AS \"%s\",\n"
;;                                        "  CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"%s\",\n"
;;                                        "  CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"%s\",\n"
;;                                 "  pg_catalog.format_type(o.oprresult, NULL) AS \"%s\",\n"
;;                       "  coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n"
;;      "           pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
;;                                        "FROM pg_catalog.pg_operator o\n"
;;        "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Left arg type"),
;;                                        gettext_noop("Right arg type"),
;;                                        gettext_noop("Result type"),
;;                                        gettext_noop("Description"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, true,
;;                                                "n.nspname", "o.oprname", NULL,
;;                                                "pg_catalog.pg_operator_is_visible(o.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3, 4;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of operators");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /*
;;  * listAllDbs
;;  *
;;  * for \l, \list, and -l switch
;;  */
;; bool
;; listAllDbs(bool verbose)
;; {
;;      PGresult   *res;
;;      PQExpBufferData buf;
;;      printQueryOpt myopt = pset.popt;
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT d.datname as \"%s\",\n"
;;                                 "       pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n"
;;                      "       pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n",
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Owner"),
;;                                        gettext_noop("Encoding"));
;;      if (pset.sversion >= 80400)
;;              appendPQExpBuffer(&buf,
;;                                                "       d.datcollate as \"%s\",\n"
;;                                                "       d.datctype as \"%s\",\n",
;;                                                gettext_noop("Collation"),
;;                                                gettext_noop("Ctype"));
;;      appendPQExpBuffer(&buf, "       ");
;;      printACLColumn(&buf, "d.datacl");
;;      if (verbose && pset.sversion >= 80200)
;;              appendPQExpBuffer(&buf,
;;                                                ",\n       CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')\n"
;;                                                "            THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n"
;;                                                "            ELSE 'No Access'\n"
;;                                                "       END as \"%s\"",
;;                                                gettext_noop("Size"));
;;      if (verbose && pset.sversion >= 80000)
;;              appendPQExpBuffer(&buf,
;;                                                ",\n       t.spcname as \"%s\"",
;;                                                gettext_noop("Tablespace"));
;;      if (verbose && pset.sversion >= 80200)
;;              appendPQExpBuffer(&buf,
;;                                                ",\n       pg_catalog.shobj_description(d.oid, 'pg_database') as \"%s\"",
;;                                                gettext_noop("Description"));
;;      appendPQExpBuffer(&buf,
;;                                        "\nFROM pg_catalog.pg_database d\n");
;;      if (verbose && pset.sversion >= 80000)
;;              appendPQExpBuffer(&buf,
;;                 "  JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n");
;;      appendPQExpBuffer(&buf, "ORDER BY 1;");
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of databases");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /*
;;  * List Tables' Grant/Revoke Permissions
;;  * \z (now also \dp -- perhaps more mnemonic)
;;  */
;; bool
;; permissionsList(const char *pattern)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;      static const bool translate_columns[] = {false, false, true, false, false};
;;
;;      initPQExpBuffer(&buf);
;;
;;      /*
;;       * we ignore indexes and toast tables since they have no meaningful rights
;;       */
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT n.nspname as \"%s\",\n"
;;                                        "  c.relname as \"%s\",\n"
;;                                        "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' END as \"%s\",\n"
;;                                        "  ",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"),
;;         gettext_noop("table"), gettext_noop("view"), gettext_noop("sequence"),
;;                                        gettext_noop("Type"));
;;
;;      printACLColumn(&buf, "c.relacl");
;;
;;      if (pset.sversion >= 80400)
;;              appendPQExpBuffer(&buf,
;;                                                ",\n  pg_catalog.array_to_string(ARRAY(\n"
;;                                                "    SELECT attname || E':\\n  ' || pg_catalog.array_to_string(attacl, E'\\n  ')\n"
;;                                                "    FROM pg_catalog.pg_attribute a\n"
;;                                                "    WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n"
;;                                                "  ), E'\\n') AS \"%s\"",
;;                                                gettext_noop("Column access privileges"));
;;
;;      appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_class c\n"
;;         "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
;;                                        "WHERE c.relkind IN ('r', 'v', 'S')\n");
;;
;;      /*
;;       * Unless a schema pattern is specified, we suppress system and temp
;;       * tables, since they normally aren't very interesting from a permissions
;;       * point of view.  You can see 'em by explicit request though, eg with \z
;;       * pg_catalog.*
;;       */
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "c.relname", NULL,
;;                      "n.nspname !~ '^pg_' AND pg_catalog.pg_table_is_visible(c.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      if (!res)
;;      {
;;              termPQExpBuffer(&buf);
;;              return false;
;;      }
;;
;;      myopt.nullPrint = NULL;
;;      printfPQExpBuffer(&buf, _("Access privileges"));
;;      myopt.title = buf.data;
;;      myopt.translate_header = true;
;;      myopt.translate_columns = translate_columns;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      termPQExpBuffer(&buf);
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;;
;; /*
;;  * Get object comments
;;  *
;;  * \dd [foo]
;;  *
;;  * Note: This only lists things that actually have a description. For complete
;;  * lists of things, there are other \d? commands.
;;  */
;; bool
;; objectDescription(const char *pattern, bool showSystem)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;      static const bool translate_columns[] = {false, false, true, false};
;;
;;      initPQExpBuffer(&buf);
;;
;;      appendPQExpBuffer(&buf,
;;                                        "SELECT DISTINCT tt.nspname AS \"%s\", tt.name AS \"%s\", tt.object AS \"%s\", d.description AS \"%s\"\n"
;;                                        "FROM (\n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Object"),
;;                                        gettext_noop("Description"));
;;
;;      /* Aggregate descriptions */
;;      appendPQExpBuffer(&buf,
;;                                        "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
;;                                        "  n.nspname as nspname,\n"
;;                                        "  CAST(p.proname AS pg_catalog.text) as name,"
;;                                        "  CAST('%s' AS pg_catalog.text) as object\n"
;;                                        "  FROM pg_catalog.pg_proc p\n"
;;       "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
;;                                        "  WHERE p.proisagg\n",
;;                                        gettext_noop("aggregate"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "p.proname", NULL,
;;                                                "pg_catalog.pg_function_is_visible(p.oid)");
;;
;;      /* Function descriptions */
;;      appendPQExpBuffer(&buf,
;;                                        "UNION ALL\n"
;;                                        "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
;;                                        "  n.nspname as nspname,\n"
;;                                        "  CAST(p.proname AS pg_catalog.text) as name,"
;;                                        "  CAST('%s' AS pg_catalog.text) as object\n"
;;                                        "  FROM pg_catalog.pg_proc p\n"
;;       "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
;;                                        "  WHERE NOT p.proisagg\n",
;;                                        gettext_noop("function"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "p.proname", NULL,
;;                                                "pg_catalog.pg_function_is_visible(p.oid)");
;;
;;      /* Operator descriptions (only if operator has its own comment) */
;;      appendPQExpBuffer(&buf,
;;                                        "UNION ALL\n"
;;                                        "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
;;                                        "  n.nspname as nspname,\n"
;;                                        "  CAST(o.oprname AS pg_catalog.text) as name,"
;;                                        "  CAST('%s' AS pg_catalog.text) as object\n"
;;                                        "  FROM pg_catalog.pg_operator o\n"
;;      "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
;;                                        gettext_noop("operator"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
;;                                                "n.nspname", "o.oprname", NULL,
;;                                                "pg_catalog.pg_operator_is_visible(o.oid)");
;;
;;      /* Type description */
;;      appendPQExpBuffer(&buf,
;;                                        "UNION ALL\n"
;;                                        "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
;;                                        "  n.nspname as nspname,\n"
;;                                        "  pg_catalog.format_type(t.oid, NULL) as name,"
;;                                        "  CAST('%s' AS pg_catalog.text) as object\n"
;;                                        "  FROM pg_catalog.pg_type t\n"
;;      "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
;;                                        gettext_noop("data type"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
;;                                                "n.nspname", "pg_catalog.format_type(t.oid, NULL)",
;;                                                NULL,
;;                                                "pg_catalog.pg_type_is_visible(t.oid)");
;;
;;      /* Relation (tables, views, indexes, sequences) descriptions */
;;      appendPQExpBuffer(&buf,
;;                                        "UNION ALL\n"
;;                                        "  SELECT c.oid as oid, c.tableoid as tableoid,\n"
;;                                        "  n.nspname as nspname,\n"
;;                                        "  CAST(c.relname AS pg_catalog.text) as name,\n"
;;                                        "  CAST(\n"
;;                                        "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' END"
;;                                        "  AS pg_catalog.text) as object\n"
;;                                        "  FROM pg_catalog.pg_class c\n"
;;       "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
;;                                        "  WHERE c.relkind IN ('r', 'v', 'i', 'S')\n",
;;                                        gettext_noop("table"),
;;                                        gettext_noop("view"),
;;                                        gettext_noop("index"),
;;                                        gettext_noop("sequence"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "c.relname", NULL,
;;                                                "pg_catalog.pg_table_is_visible(c.oid)");
;;
;;      /* Rule description (ignore rules for views) */
;;      appendPQExpBuffer(&buf,
;;                                        "UNION ALL\n"
;;                                        "  SELECT r.oid as oid, r.tableoid as tableoid,\n"
;;                                        "  n.nspname as nspname,\n"
;;                                        "  CAST(r.rulename AS pg_catalog.text) as name,"
;;                                        "  CAST('%s' AS pg_catalog.text) as object\n"
;;                                        "  FROM pg_catalog.pg_rewrite r\n"
;;                                "       JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n"
;;       "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
;;                                        "  WHERE r.rulename != '_RETURN'\n",
;;                                        gettext_noop("rule"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      /* XXX not sure what to do about visibility rule here? */
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "r.rulename", NULL,
;;                                                "pg_catalog.pg_table_is_visible(c.oid)");
;;
;;      /* Trigger description */
;;      appendPQExpBuffer(&buf,
;;                                        "UNION ALL\n"
;;                                        "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
;;                                        "  n.nspname as nspname,\n"
;;                                        "  CAST(t.tgname AS pg_catalog.text) as name,"
;;                                        "  CAST('%s' AS pg_catalog.text) as object\n"
;;                                        "  FROM pg_catalog.pg_trigger t\n"
;;                                 "       JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n"
;;      "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n",
;;                                        gettext_noop("trigger"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      /* XXX not sure what to do about visibility rule here? */
;;      processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
;;                                                "n.nspname", "t.tgname", NULL,
;;                                                "pg_catalog.pg_table_is_visible(c.oid)");
;;
;;      appendPQExpBuffer(&buf,
;;                                        ") AS tt\n"
;;                                        "  JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("Object descriptions");
;;      myopt.translate_header = true;
;;      myopt.translate_columns = translate_columns;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /*
;;  * describeTableDetails (for \d)
;;  *
;;  * This routine finds the tables to be displayed, and calls
;;  * describeOneTableDetails for each one.
;;  *
;;  * verbose: if true, this is \d+
;;  */
;; bool
;; describeTableDetails(const char *pattern, bool verbose, bool showSystem)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      int                     i;
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT c.oid,\n"
;;                                        "  n.nspname,\n"
;;                                        "  c.relname\n"
;;                                        "FROM pg_catalog.pg_class c\n"
;;       "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
;;                                                "n.nspname", "c.relname", NULL,
;;                                                "pg_catalog.pg_table_is_visible(c.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 2, 3;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      if (PQntuples(res) == 0)
;;      {
;;              if (!pset.quiet)
;;                      fprintf(stderr, _("Did not find any relation named \"%s\".\n"),
;;                                      pattern);
;;              PQclear(res);
;;              return false;
;;      }
;;
;;      for (i = 0; i < PQntuples(res); i++)
;;      {
;;              const char *oid;
;;              const char *nspname;
;;              const char *relname;
;;
;;              oid = PQgetvalue(res, i, 0);
;;              nspname = PQgetvalue(res, i, 1);
;;              relname = PQgetvalue(res, i, 2);
;;
;;              if (!describeOneTableDetails(nspname, relname, oid, verbose))
;;              {
;;                      PQclear(res);
;;                      return false;
;;              }
;;              if (cancel_pressed)
;;              {
;;                      PQclear(res);
;;                      return false;
;;              }
;;      }
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; /*
;;  * describeOneTableDetails (for \d)
;;  *
;;  * Unfortunately, the information presented here is so complicated that it
;;  * cannot be done in a single query. So we have to assemble the printed table
;;  * by hand and pass it to the underlying printTable() function.
;;  */
;; static bool
;; describeOneTableDetails(const char *schemaname,
;;                                              const char *relationname,
;;                                              const char *oid,
;;                                              bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res = NULL;
;;      printTableOpt myopt = pset.popt.topt;
;;      printTableContent cont;
;;      bool            printTableInitialized = false;
;;      int                     i;
;;      char       *view_def = NULL;
;;      char       *headers[6];
;;      char      **seq_values = NULL;
;;      char      **modifiers = NULL;
;;      char      **ptr;
;;      PQExpBufferData title;
;;      PQExpBufferData tmpbuf;
;;      int                     cols = 0;
;;      int                     numrows = 0;
;;      struct
;;      {
;;              int16           checks;
;;              char            relkind;
;;              bool            hasindex;
;;              bool            hasrules;
;;              bool            hastriggers;
;;              bool            hasoids;
;;              Oid                     tablespace;
;;              char       *reloptions;
;;      }                       tableinfo;
;;      bool            show_modifiers = false;
;;      bool            retval;
;;
;;      retval = false;
;;
;;      /* This output looks confusing in expanded mode. */
;;      myopt.expanded = false;
;;
;;      initPQExpBuffer(&buf);
;;      initPQExpBuffer(&title);
;;      initPQExpBuffer(&tmpbuf);
;;
;;      /* Get general table info */
;;      if (pset.sversion >= 80400)
;;      {
;;              printfPQExpBuffer(&buf,
;;                        "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
;;                                                "c.relhastriggers, c.relhasoids, "
;;                                                "%s, c.reltablespace\n"
;;                                                "FROM pg_catalog.pg_class c\n "
;;                 "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
;;                                                "WHERE c.oid = '%s'\n",
;;                                                (verbose ?
;;                                                 "pg_catalog.array_to_string(c.reloptions || "
;;                                                 "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
;;                                                 : "''"),
;;                                                oid);
;;      }
;;      else if (pset.sversion >= 80200)
;;      {
;;              printfPQExpBuffer(&buf,
;;                                        "SELECT relchecks, relkind, relhasindex, relhasrules, "
;;                                                "reltriggers <> 0, relhasoids, "
;;                                                "%s, reltablespace\n"
;;                                                "FROM pg_catalog.pg_class WHERE oid = '%s'",
;;                                                (verbose ?
;;                                       "pg_catalog.array_to_string(reloptions, E', ')" : "''"),
;;                                                oid);
;;      }
;;      else if (pset.sversion >= 80000)
;;      {
;;              printfPQExpBuffer(&buf,
;;                                        "SELECT relchecks, relkind, relhasindex, relhasrules, "
;;                                                "reltriggers <> 0, relhasoids, "
;;                                                "'', reltablespace\n"
;;                                                "FROM pg_catalog.pg_class WHERE oid = '%s'",
;;                                                oid);
;;      }
;;      else
;;      {
;;              printfPQExpBuffer(&buf,
;;                                        "SELECT relchecks, relkind, relhasindex, relhasrules, "
;;                                                "reltriggers <> 0, relhasoids, "
;;                                                "'', ''\n"
;;                                                "FROM pg_catalog.pg_class WHERE oid = '%s'",
;;                                                oid);
;;      }
;;
;;      res = PSQLexec(buf.data, false);
;;      if (!res)
;;              goto error_return;
;;
;;      /* Did we get anything? */
;;      if (PQntuples(res) == 0)
;;      {
;;              if (!pset.quiet)
;;                      fprintf(stderr, _("Did not find any relation with OID %s.\n"),
;;                                      oid);
;;              goto error_return;
;;      }
;;
;;      tableinfo.checks = atoi(PQgetvalue(res, 0, 0));
;;      tableinfo.relkind = *(PQgetvalue(res, 0, 1));
;;      tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
;;      tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
;;      tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
;;      tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
;;      tableinfo.reloptions = pset.sversion >= 80200 ?
;;              strdup(PQgetvalue(res, 0, 6)) : 0;
;;      tableinfo.tablespace = (pset.sversion >= 80000) ?
;;              atooid(PQgetvalue(res, 0, 7)) : 0;
;;      PQclear(res);
;;      res = NULL;
;;
;;      /*
;;       * If it's a sequence, fetch its values and store into an array that will
;;       * be used later.
;;       */
;;      if (tableinfo.relkind == 'S')
;;      {
;;              printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
;;              /* must be separate because fmtId isn't reentrant */
;;              appendPQExpBuffer(&buf, ".%s", fmtId(relationname));
;;
;;              res = PSQLexec(buf.data, false);
;;              if (!res)
;;                      goto error_return;
;;
;;              seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
;;
;;              for (i = 0; i < PQnfields(res); i++)
;;                      seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
;;              seq_values[i] = NULL;
;;
;;              PQclear(res);
;;              res = NULL;
;;      }
;;
;;      /* Get column info (index requires additional checks) */
;;      printfPQExpBuffer(&buf, "SELECT a.attname,");
;;      appendPQExpBuffer(&buf, "\n  pg_catalog.format_type(a.atttypid, a.atttypmod),"
;;                                        "\n  (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)"
;;                                        "\n   FROM pg_catalog.pg_attrdef d"
;;                                        "\n   WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),"
;;                                        "\n  a.attnotnull, a.attnum");
;;      if (verbose)
;;              appendPQExpBuffer(&buf, ", a.attstorage, pg_catalog.col_description(a.attrelid, a.attnum)");
;;      appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_attribute a");
;;      if (tableinfo.relkind == 'i')
;;              appendPQExpBuffer(&buf, ", pg_catalog.pg_index i");
;;      appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
;;      if (tableinfo.relkind == 'i')
;;              appendPQExpBuffer(&buf, " AND a.attrelid = i.indexrelid");
;;      appendPQExpBuffer(&buf, "\nORDER BY a.attnum");
;;
;;      res = PSQLexec(buf.data, false);
;;      if (!res)
;;              goto error_return;
;;      numrows = PQntuples(res);
;;
;;      /* Make title */
;;      switch (tableinfo.relkind)
;;      {
;;              case 'r':
;;                      printfPQExpBuffer(&title, _("Table \"%s.%s\""),
;;                                                        schemaname, relationname);
;;                      break;
;;              case 'v':
;;                      printfPQExpBuffer(&title, _("View \"%s.%s\""),
;;                                                        schemaname, relationname);
;;                      break;
;;              case 'S':
;;                      printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
;;                                                        schemaname, relationname);
;;                      break;
;;              case 'i':
;;                      printfPQExpBuffer(&title, _("Index \"%s.%s\""),
;;                                                        schemaname, relationname);
;;                      break;
;;              case 's':
;;                      /* not used as of 8.2, but keep it for backwards compatibility */
;;                      printfPQExpBuffer(&title, _("Special relation \"%s.%s\""),
;;                                                        schemaname, relationname);
;;                      break;
;;              case 't':
;;                      printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
;;                                                        schemaname, relationname);
;;                      break;
;;              case 'c':
;;                      printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
;;                                                        schemaname, relationname);
;;                      break;
;;              default:
;;                      /* untranslated unknown relkind */
;;                      printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
;;                                                        tableinfo.relkind, schemaname, relationname);
;;                      break;
;;      }
;;
;;      /* Set the number of columns, and their names */
;;      cols += 2;
;;      headers[0] = gettext_noop("Column");
;;      headers[1] = gettext_noop("Type");
;;
;;      if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v')
;;      {
;;              show_modifiers = true;
;;              headers[cols++] = gettext_noop("Modifiers");
;;              modifiers = pg_malloc_zero((numrows + 1) * sizeof(*modifiers));
;;      }
;;
;;      if (tableinfo.relkind == 'S')
;;              headers[cols++] = gettext_noop("Value");
;;
;;      if (verbose)
;;      {
;;              headers[cols++] = gettext_noop("Storage");
;;              headers[cols++] = gettext_noop("Description");
;;      }
;;
;;      printTableInit(&cont, &myopt, title.data, cols, numrows);
;;      printTableInitialized = true;
;;
;;      for (i = 0; i < cols; i++)
;;              printTableAddHeader(&cont, headers[i], true, 'l');
;;
;;      /* Check if table is a view */
;;      if (tableinfo.relkind == 'v')
;;      {
;;              PGresult   *result;
;;
;;              printfPQExpBuffer(&buf,
;;                        "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true)",
;;                                                oid);
;;              result = PSQLexec(buf.data, false);
;;              if (!result)
;;                      goto error_return;
;;
;;              if (PQntuples(result) > 0)
;;                      view_def = pg_strdup(PQgetvalue(result, 0, 0));
;;
;;              PQclear(result);
;;      }
;;
;;      /* Generate table cells to be printed */
;;      for (i = 0; i < numrows; i++)
;;      {
;;              /* Column */
;;              printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
;;
;;              /* Type */
;;              printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false);
;;
;;              /* Modifiers: not null and default */
;;              if (show_modifiers)
;;              {
;;                      resetPQExpBuffer(&tmpbuf);
;;                      if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
;;                              appendPQExpBufferStr(&tmpbuf, _("not null"));
;;
;;                      /* handle "default" here */
;;                      /* (note: above we cut off the 'default' string at 128) */
;;                      if (strlen(PQgetvalue(res, i, 2)) != 0)
;;                      {
;;                              if (tmpbuf.len > 0)
;;                                      appendPQExpBufferStr(&tmpbuf, " ");
;;                              /* translator: default values of column definitions */
;;                              appendPQExpBuffer(&tmpbuf, _("default %s"),
;;                                                                PQgetvalue(res, i, 2));
;;                      }
;;
;;                      modifiers[i] = pg_strdup(tmpbuf.data);
;;                      printTableAddCell(&cont, modifiers[i], false, false);
;;              }
;;
;;              /* Value: for sequences only */
;;              if (tableinfo.relkind == 'S')
;;                      printTableAddCell(&cont, seq_values[i], false, false);
;;
;;              /* Storage and Description */
;;              if (verbose)
;;              {
;;                      char       *storage = PQgetvalue(res, i, 5);
;;
;;                      /* these strings are literal in our syntax, so not translated. */
;;                      printTableAddCell(&cont, (storage[0] == 'p' ? "plain" :
;;                                                                        (storage[0] == 'm' ? "main" :
;;                                                                         (storage[0] == 'x' ? "extended" :
;;                                                                              (storage[0] == 'e' ? "external" :
;;                                                                               "???")))),
;;                                                        false, false);
;;                      printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
;;              }
;;      }
;;
;;      /* Make footers */
;;      if (tableinfo.relkind == 'i')
;;      {
;;              /* Footer information about an index */
;;              PGresult   *result;
;;
;;              printfPQExpBuffer(&buf,
;;                               "SELECT i.indisunique, i.indisprimary, i.indisclustered, ");
;;              if (pset.sversion >= 80200)
;;                      appendPQExpBuffer(&buf, "i.indisvalid, ");
;;              else
;;                      appendPQExpBuffer(&buf, "true as indisvalid, ");
;;              appendPQExpBuffer(&buf, "a.amname, c2.relname,\n"
;;                                      "  pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
;;                                                "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
;;                "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
;;                                                "AND i.indrelid = c2.oid",
;;                                                oid);
;;
;;              result = PSQLexec(buf.data, false);
;;              if (!result)
;;                      goto error_return;
;;              else if (PQntuples(result) != 1)
;;              {
;;                      PQclear(result);
;;                      goto error_return;
;;              }
;;              else
;;              {
;;                      char       *indisunique = PQgetvalue(result, 0, 0);
;;                      char       *indisprimary = PQgetvalue(result, 0, 1);
;;                      char       *indisclustered = PQgetvalue(result, 0, 2);
;;                      char       *indisvalid = PQgetvalue(result, 0, 3);
;;                      char       *indamname = PQgetvalue(result, 0, 4);
;;                      char       *indtable = PQgetvalue(result, 0, 5);
;;                      char       *indpred = PQgetvalue(result, 0, 6);
;;
;;                      if (strcmp(indisprimary, "t") == 0)
;;                              printfPQExpBuffer(&tmpbuf, _("primary key, "));
;;                      else if (strcmp(indisunique, "t") == 0)
;;                              printfPQExpBuffer(&tmpbuf, _("unique, "));
;;                      else
;;                              resetPQExpBuffer(&tmpbuf);
;;                      appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
;;
;;                      /* we assume here that index and table are in same schema */
;;                      appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""),
;;                                                        schemaname, indtable);
;;
;;                      if (strlen(indpred))
;;                              appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred);
;;
;;                      if (strcmp(indisclustered, "t") == 0)
;;                              appendPQExpBuffer(&tmpbuf, _(", clustered"));
;;
;;                      if (strcmp(indisvalid, "t") != 0)
;;                              appendPQExpBuffer(&tmpbuf, _(", invalid"));
;;
;;                      printTableAddFooter(&cont, tmpbuf.data);
;;                      add_tablespace_footer(&cont, tableinfo.relkind,
;;                                                                tableinfo.tablespace, true);
;;              }
;;
;;              PQclear(result);
;;      }
;;      else if (view_def)
;;      {
;;              PGresult   *result = NULL;
;;
;;              /* Footer information about a view */
;;              printTableAddFooter(&cont, _("View definition:"));
;;              printTableAddFooter(&cont, view_def);
;;
;;              /* print rules */
;;              if (tableinfo.hasrules)
;;              {
;;                      printfPQExpBuffer(&buf,
;;                                                        "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
;;                                                        "FROM pg_catalog.pg_rewrite r\n"
;;                      "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1",
;;                                                        oid);
;;                      result = PSQLexec(buf.data, false);
;;                      if (!result)
;;                              goto error_return;
;;
;;                      if (PQntuples(result) > 0)
;;                      {
;;                              printTableAddFooter(&cont, _("Rules:"));
;;                              for (i = 0; i < PQntuples(result); i++)
;;                              {
;;                                      const char *ruledef;
;;
;;                                      /* Everything after "CREATE RULE" is echoed verbatim */
;;                                      ruledef = PQgetvalue(result, i, 1);
;;                                      ruledef += 12;
;;
;;                                      printfPQExpBuffer(&buf, " %s", ruledef);
;;                                      printTableAddFooter(&cont, buf.data);
;;                              }
;;                      }
;;                      PQclear(result);
;;              }
;;      }
;;      else if (tableinfo.relkind == 'r')
;;      {
;;              /* Footer information about a table */
;;              PGresult   *result = NULL;
;;              int                     tuples = 0;
;;
;;              /* print indexes */
;;              if (tableinfo.hasindex)
;;              {
;;                      printfPQExpBuffer(&buf,
;;                                                        "SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, ");
;;                      if (pset.sversion >= 80200)
;;                              appendPQExpBuffer(&buf, "i.indisvalid, ");
;;                      else
;;                              appendPQExpBuffer(&buf, "true as indisvalid, ");
;;                      appendPQExpBuffer(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)");
;;                      if (pset.sversion >= 80000)
;;                              appendPQExpBuffer(&buf, ", c2.reltablespace");
;;                      appendPQExpBuffer(&buf,
;;                                                        "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
;;                                                        "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
;;                        "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname",
;;                                                        oid);
;;                      result = PSQLexec(buf.data, false);
;;                      if (!result)
;;                              goto error_return;
;;                      else
;;                              tuples = PQntuples(result);
;;
;;                      if (tuples > 0)
;;                      {
;;                              printTableAddFooter(&cont, _("Indexes:"));
;;                              for (i = 0; i < tuples; i++)
;;                              {
;;                                      const char *indexdef;
;;                                      const char *usingpos;
;;
;;                                      /* untranslated index name */
;;                                      printfPQExpBuffer(&buf, "    \"%s\"",
;;                                                                        PQgetvalue(result, i, 0));
;;
;;                                      /* Label as primary key or unique (but not both) */
;;                                      appendPQExpBuffer(&buf,
;;                                                                 strcmp(PQgetvalue(result, i, 1), "t") == 0
;;                                                                        ? " PRIMARY KEY," :
;;                                                                (strcmp(PQgetvalue(result, i, 2), "t") == 0
;;                                                                 ? " UNIQUE,"
;;                                                                 : ""));
;;                                      /* Everything after "USING" is echoed verbatim */
;;                                      indexdef = PQgetvalue(result, i, 5);
;;                                      usingpos = strstr(indexdef, " USING ");
;;                                      if (usingpos)
;;                                              indexdef = usingpos + 7;
;;
;;                                      appendPQExpBuffer(&buf, " %s", indexdef);
;;
;;                                      if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
;;                                              appendPQExpBuffer(&buf, " CLUSTER");
;;
;;                                      if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
;;                                              appendPQExpBuffer(&buf, " INVALID");
;;
;;                                      printTableAddFooter(&cont, buf.data);
;;
;;                                      /* Print tablespace of the index on the same line */
;;                                      if (pset.sversion >= 80000)
;;                                              add_tablespace_footer(&cont, 'i',
;;                                                                                      atooid(PQgetvalue(result, i, 6)),
;;                                                                                        false);
;;                              }
;;                      }
;;                      PQclear(result);
;;              }
;;
;;              /* print table (and column) check constraints */
;;              if (tableinfo.checks)
;;              {
;;                      printfPQExpBuffer(&buf,
;;                                                        "SELECT r.conname, "
;;                                                        "pg_catalog.pg_get_constraintdef(r.oid, true)\n"
;;                                                        "FROM pg_catalog.pg_constraint r\n"
;;                                 "WHERE r.conrelid = '%s' AND r.contype = 'c'\nORDER BY 1",
;;                                                        oid);
;;                      result = PSQLexec(buf.data, false);
;;                      if (!result)
;;                              goto error_return;
;;                      else
;;                              tuples = PQntuples(result);
;;
;;                      if (tuples > 0)
;;                      {
;;                              printTableAddFooter(&cont, _("Check constraints:"));
;;                              for (i = 0; i < tuples; i++)
;;                              {
;;                                      /* untranslated contraint name and def */
;;                                      printfPQExpBuffer(&buf, "    \"%s\" %s",
;;                                                                        PQgetvalue(result, i, 0),
;;                                                                        PQgetvalue(result, i, 1));
;;
;;                                      printTableAddFooter(&cont, buf.data);
;;                              }
;;                      }
;;                      PQclear(result);
;;              }
;;
;;              /* print foreign-key constraints (there are none if no triggers) */
;;              if (tableinfo.hastriggers)
;;              {
;;                      printfPQExpBuffer(&buf,
;;                                                        "SELECT conname,\n"
;;                               "  pg_catalog.pg_get_constraintdef(r.oid, true) as condef\n"
;;                                                        "FROM pg_catalog.pg_constraint r\n"
;;                                      "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1",
;;                                                        oid);
;;                      result = PSQLexec(buf.data, false);
;;                      if (!result)
;;                              goto error_return;
;;                      else
;;                              tuples = PQntuples(result);
;;
;;                      if (tuples > 0)
;;                      {
;;                              printTableAddFooter(&cont, _("Foreign-key constraints:"));
;;                              for (i = 0; i < tuples; i++)
;;                              {
;;                                      /* untranslated constraint name and def */
;;                                      printfPQExpBuffer(&buf, "    \"%s\" %s",
;;                                                                        PQgetvalue(result, i, 0),
;;                                                                        PQgetvalue(result, i, 1));
;;
;;                                      printTableAddFooter(&cont, buf.data);
;;                              }
;;                      }
;;                      PQclear(result);
;;              }
;;
;;              /* print incoming foreign-key references (none if no triggers) */
;;              if (tableinfo.hastriggers)
;;              {
;;                      printfPQExpBuffer(&buf,
;;                                                 "SELECT conname, conrelid::pg_catalog.regclass,\n"
;;                               "  pg_catalog.pg_get_constraintdef(c.oid, true) as condef\n"
;;                                                        "FROM pg_catalog.pg_constraint c\n"
;;                                 "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1",
;;                                                        oid);
;;                      result = PSQLexec(buf.data, false);
;;                      if (!result)
;;                              goto error_return;
;;                      else
;;                              tuples = PQntuples(result);
;;
;;                      if (tuples > 0)
;;                      {
;;                              printTableAddFooter(&cont, _("Referenced by:"));
;;                              for (i = 0; i < tuples; i++)
;;                              {
;;                                      printfPQExpBuffer(&buf, "    TABLE \"%s\" CONSTRAINT \"%s\" %s",
;;                                                                        PQgetvalue(result, i, 1),
;;                                                                        PQgetvalue(result, i, 0),
;;                                                                        PQgetvalue(result, i, 2));
;;
;;                                      printTableAddFooter(&cont, buf.data);
;;                              }
;;                      }
;;                      PQclear(result);
;;              }
;;
;;              /* print rules */
;;              if (tableinfo.hasrules)
;;              {
;;                      if (pset.sversion >= 80300)
;;                      {
;;                              printfPQExpBuffer(&buf,
;;                                                                "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
;;                                                                "ev_enabled\n"
;;                                                                "FROM pg_catalog.pg_rewrite r\n"
;;                                                                "WHERE r.ev_class = '%s' ORDER BY 1",
;;                                                                oid);
;;                      }
;;                      else
;;                      {
;;                              printfPQExpBuffer(&buf,
;;                                                                "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
;;                                                                "'O'::char AS ev_enabled\n"
;;                                                                "FROM pg_catalog.pg_rewrite r\n"
;;                                                                "WHERE r.ev_class = '%s' ORDER BY 1",
;;                                                                oid);
;;                      }
;;                      result = PSQLexec(buf.data, false);
;;                      if (!result)
;;                              goto error_return;
;;                      else
;;                              tuples = PQntuples(result);
;;
;;                      if (tuples > 0)
;;                      {
;;                              bool            have_heading;
;;                              int                     category;
;;
;;                              for (category = 0; category < 4; category++)
;;                              {
;;                                      have_heading = false;
;;
;;                                      for (i = 0; i < tuples; i++)
;;                                      {
;;                                              const char *ruledef;
;;                                              bool            list_rule = false;
;;
;;                                              switch (category)
;;                                              {
;;                                                      case 0:
;;                                                              if (*PQgetvalue(result, i, 2) == 'O')
;;                                                                      list_rule = true;
;;                                                              break;
;;                                                      case 1:
;;                                                              if (*PQgetvalue(result, i, 2) == 'D')
;;                                                                      list_rule = true;
;;                                                              break;
;;                                                      case 2:
;;                                                              if (*PQgetvalue(result, i, 2) == 'A')
;;                                                                      list_rule = true;
;;                                                              break;
;;                                                      case 3:
;;                                                              if (*PQgetvalue(result, i, 2) == 'R')
;;                                                                      list_rule = true;
;;                                                              break;
;;                                              }
;;                                              if (!list_rule)
;;                                                      continue;
;;
;;                                              if (!have_heading)
;;                                              {
;;                                                      switch (category)
;;                                                      {
;;                                                              case 0:
;;                                                                      printfPQExpBuffer(&buf, _("Rules:"));
;;                                                                      break;
;;                                                              case 1:
;;                                                                      printfPQExpBuffer(&buf, _("Disabled rules:"));
;;                                                                      break;
;;                                                              case 2:
;;                                                                      printfPQExpBuffer(&buf, _("Rules firing always:"));
;;                                                                      break;
;;                                                              case 3:
;;                                                                      printfPQExpBuffer(&buf, _("Rules firing on replica only:"));
;;                                                                      break;
;;                                                      }
;;                                                      printTableAddFooter(&cont, buf.data);
;;                                                      have_heading = true;
;;                                              }
;;
;;                                              /* Everything after "CREATE RULE" is echoed verbatim */
;;                                              ruledef = PQgetvalue(result, i, 1);
;;                                              ruledef += 12;
;;                                              printfPQExpBuffer(&buf, "    %s", ruledef);
;;                                              printTableAddFooter(&cont, buf.data);
;;                                      }
;;                              }
;;                      }
;;                      PQclear(result);
;;              }
;;
;;              /* print triggers (but ignore foreign-key triggers) */
;;              if (tableinfo.hastriggers)
;;              {
;;                      printfPQExpBuffer(&buf,
;;                                                        "SELECT t.tgname, "
;;                                                        "pg_catalog.pg_get_triggerdef(t.oid), "
;;                                                        "t.tgenabled\n"
;;                                                        "FROM pg_catalog.pg_trigger t\n"
;;                                                        "WHERE t.tgrelid = '%s' AND ",
;;                                                        oid);
;;                      if (pset.sversion >= 80300)
;;                              appendPQExpBuffer(&buf, "t.tgconstraint = 0");
;;                      else
;;                              appendPQExpBuffer(&buf,
;;                                                                "(NOT tgisconstraint "
;;                                                                " OR NOT EXISTS"
;;                                                                "  (SELECT 1 FROM pg_catalog.pg_depend d "
;;                                                                "   JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
;;                                                                "   WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))");
;;                      appendPQExpBuffer(&buf, "\nORDER BY 1");
;;
;;                      result = PSQLexec(buf.data, false);
;;                      if (!result)
;;                              goto error_return;
;;                      else
;;                              tuples = PQntuples(result);
;;
;;                      if (tuples > 0)
;;                      {
;;                              bool            have_heading;
;;                              int                     category;
;;
;;                              /*
;;                               * split the output into 4 different categories. Enabled
;;                               * triggers, disabled triggers and the two special ALWAYS and
;;                               * REPLICA configurations.
;;                               */
;;                              for (category = 0; category < 4; category++)
;;                              {
;;                                      have_heading = false;
;;                                      for (i = 0; i < tuples; i++)
;;                                      {
;;                                              bool            list_trigger;
;;                                              const char *tgdef;
;;                                              const char *usingpos;
;;                                              const char *tgenabled;
;;
;;                                              /*
;;                                               * Check if this trigger falls into the current
;;                                               * category
;;                                               */
;;                                              tgenabled = PQgetvalue(result, i, 2);
;;                                              list_trigger = false;
;;                                              switch (category)
;;                                              {
;;                                                      case 0:
;;                                                              if (*tgenabled == 'O' || *tgenabled == 't')
;;                                                                      list_trigger = true;
;;                                                              break;
;;                                                      case 1:
;;                                                              if (*tgenabled == 'D' || *tgenabled == 'f')
;;                                                                      list_trigger = true;
;;                                                              break;
;;                                                      case 2:
;;                                                              if (*tgenabled == 'A')
;;                                                                      list_trigger = true;
;;                                                              break;
;;                                                      case 3:
;;                                                              if (*tgenabled == 'R')
;;                                                                      list_trigger = true;
;;                                                              break;
;;                                              }
;;                                              if (list_trigger == false)
;;                                                      continue;
;;
;;                                              /* Print the category heading once */
;;                                              if (have_heading == false)
;;                                              {
;;                                                      switch (category)
;;                                                      {
;;                                                              case 0:
;;                                                                      printfPQExpBuffer(&buf, _("Triggers:"));
;;                                                                      break;
;;                                                              case 1:
;;                                                                      printfPQExpBuffer(&buf, _("Disabled triggers:"));
;;                                                                      break;
;;                                                              case 2:
;;                                                                      printfPQExpBuffer(&buf, _("Triggers firing always:"));
;;                                                                      break;
;;                                                              case 3:
;;                                                                      printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
;;                                                                      break;
;;
;;                                                      }
;;                                                      printTableAddFooter(&cont, buf.data);
;;                                                      have_heading = true;
;;                                              }
;;
;;                                              /* Everything after "TRIGGER" is echoed verbatim */
;;                                              tgdef = PQgetvalue(result, i, 1);
;;                                              usingpos = strstr(tgdef, " TRIGGER ");
;;                                              if (usingpos)
;;                                                      tgdef = usingpos + 9;
;;
;;                                              printfPQExpBuffer(&buf, "    %s", tgdef);
;;                                              printTableAddFooter(&cont, buf.data);
;;                                      }
;;                              }
;;                      }
;;                      PQclear(result);
;;              }
;;
;;              /* print inherited tables */
;;              printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid);
;;
;;              result = PSQLexec(buf.data, false);
;;              if (!result)
;;                      goto error_return;
;;              else
;;                      tuples = PQntuples(result);
;;
;;              for (i = 0; i < tuples; i++)
;;              {
;;                      const char *s = _("Inherits");
;;
;;                      if (i == 0)
;;                              printfPQExpBuffer(&buf, "%s: %s", s, PQgetvalue(result, i, 0));
;;                      else
;;                              printfPQExpBuffer(&buf, "%*s  %s", (int) strlen(s), "", PQgetvalue(result, i, 0));
;;                      if (i < tuples - 1)
;;                              appendPQExpBuffer(&buf, ",");
;;
;;                      printTableAddFooter(&cont, buf.data);
;;              }
;;              PQclear(result);
;;
;;              if (verbose)
;;              {
;;                      const char *s = _("Has OIDs");
;;
;;                      printfPQExpBuffer(&buf, "%s: %s", s,
;;                                                        (tableinfo.hasoids ? _("yes") : _("no")));
;;                      printTableAddFooter(&cont, buf.data);
;;
;;                      /* print reloptions */
;;                      if (pset.sversion >= 80200)
;;                      {
;;                              if (tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
;;                              {
;;                                      const char *t = _("Options");
;;
;;                                      printfPQExpBuffer(&buf, "%s: %s", t,
;;                                                                        tableinfo.reloptions);
;;                                      printTableAddFooter(&cont, buf.data);
;;                              }
;;                      }
;;              }
;;
;;              add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
;;                                                        true);
;;      }
;;
;;      printTable(&cont, pset.queryFout, pset.logfile);
;;      printTableCleanup(&cont);
;;
;;      retval = true;
;;
;; error_return:
;;
;;      /* clean up */
;;      if (printTableInitialized)
;;              printTableCleanup(&cont);
;;      termPQExpBuffer(&buf);
;;      termPQExpBuffer(&title);
;;      termPQExpBuffer(&tmpbuf);
;;
;;      if (seq_values)
;;      {
;;              for (ptr = seq_values; *ptr; ptr++)
;;                      free(*ptr);
;;              free(seq_values);
;;      }
;;
;;      if (modifiers)
;;      {
;;              for (ptr = modifiers; *ptr; ptr++)
;;                      free(*ptr);
;;              free(modifiers);
;;      }
;;
;;      if (view_def)
;;              free(view_def);
;;
;;      if (res)
;;              PQclear(res);
;;
;;      return retval;
;; }
;;
;; /*
;;  * Add a tablespace description to a footer.  If 'newline' is true, it is added
;;  * in a new line; otherwise it's appended to the current value of the last
;;  * footer.
;;  */
;; static void
;; add_tablespace_footer(printTableContent *const cont, char relkind,
;;                                        Oid tablespace, const bool newline)
;; {
;;      /* relkinds for which we support tablespaces */
;;      if (relkind == 'r' || relkind == 'i')
;;      {
;;              /*
;;               * We ignore the database default tablespace so that users not using
;;               * tablespaces don't need to know about them.  This case also covers
;;               * pre-8.0 servers, for which tablespace will always be 0.
;;               */
;;              if (tablespace != 0)
;;              {
;;                      PGresult   *result = NULL;
;;                      PQExpBufferData buf;
;;
;;                      initPQExpBuffer(&buf);
;;                      printfPQExpBuffer(&buf,
;;                                                        "SELECT spcname FROM pg_catalog.pg_tablespace\n"
;;                                                        "WHERE oid = '%u'", tablespace);
;;                      result = PSQLexec(buf.data, false);
;;                      if (!result)
;;                              return;
;;                      /* Should always be the case, but.... */
;;                      if (PQntuples(result) > 0)
;;                      {
;;                              if (newline)
;;                              {
;;                                      /* Add the tablespace as a new footer */
;;                                      printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
;;                                                                        PQgetvalue(result, 0, 0));
;;                                      printTableAddFooter(cont, buf.data);
;;                              }
;;                              else
;;                              {
;;                                      /* Append the tablespace to the latest footer */
;;                                      printfPQExpBuffer(&buf, "%s", cont->footer->data);
;;
;;                                      /*
;;                                       * translator: before this string there's an index
;;                                       * description like '"foo_pkey" PRIMARY KEY, btree (a)'
;;                                       */
;;                                      appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
;;                                                                        PQgetvalue(result, 0, 0));
;;                                      printTableSetFooter(cont, buf.data);
;;                              }
;;                      }
;;                      PQclear(result);
;;                      termPQExpBuffer(&buf);
;;              }
;;      }
;; }
;;
;; /*
;;  * \du or \dg
;;  *
;;  * Describes roles.  Any schema portion of the pattern is ignored.
;;  */
;; bool
;; describeRoles(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printTableContent cont;
;;      printTableOpt myopt = pset.popt.topt;
;;      int                     ncols = 3;
;;      int                     nrows = 0;
;;      int                     i;
;;      int                     conns;
;;      const char      align = 'l';
;;      char      **attr;
;;
;;      initPQExpBuffer(&buf);
;;
;;      if (pset.sversion >= 80100)
;;      {
;;              printfPQExpBuffer(&buf,
;;                                                "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
;;                                                "  r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
;;                                                "  r.rolconnlimit,\n"
;;                                                "  ARRAY(SELECT b.rolname\n"
;;                                                "        FROM pg_catalog.pg_auth_members m\n"
;;                               "        JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)\n"
;;                                                "        WHERE m.member = r.oid) as memberof");
;;
;;              if (verbose && pset.sversion >= 80200)
;;              {
;;                      appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
;;                      ncols++;
;;              }
;;
;;              appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
;;
;;              processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                        NULL, "r.rolname", NULL, NULL);
;;      }
;;      else
;;      {
;;              printfPQExpBuffer(&buf,
;;                                                "SELECT u.usename AS rolname,\n"
;;                                                "  u.usesuper AS rolsuper,\n"
;;                                                "  true AS rolinherit, false AS rolcreaterole,\n"
;;                                       "  u.usecreatedb AS rolcreatedb, true AS rolcanlogin,\n"
;;                                                "  -1 AS rolconnlimit,\n"
;;                                                "  ARRAY(SELECT g.groname FROM pg_catalog.pg_group g WHERE u.usesysid = ANY(g.grolist)) as memberof"
;;                                                "\nFROM pg_catalog.pg_user u\n");
;;
;;              processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                        NULL, "u.usename", NULL, NULL);
;;      }
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1;");
;;
;;      res = PSQLexec(buf.data, false);
;;      if (!res)
;;              return false;
;;
;;      nrows = PQntuples(res);
;;      attr = pg_malloc_zero((nrows + 1) * sizeof(*attr));
;;
;;      printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
;;
;;      printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
;;      printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
;;      printTableAddHeader(&cont, gettext_noop("Member of"), true, align);
;;
;;      if (verbose && pset.sversion >= 80200)
;;              printTableAddHeader(&cont, gettext_noop("Description"), true, align);
;;
;;      for (i = 0; i < nrows; i++)
;;      {
;;              printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
;;
;;              resetPQExpBuffer(&buf);
;;              if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
;;                      add_role_attribute(&buf, _("Superuser"));
;;
;;              if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
;;                      add_role_attribute(&buf, _("No inheritance"));
;;
;;              if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
;;                      add_role_attribute(&buf, _("Create role"));
;;
;;              if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
;;                      add_role_attribute(&buf, _("Create DB"));
;;
;;              if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
;;                      add_role_attribute(&buf, _("Cannot login"));
;;
;;              conns = atoi(PQgetvalue(res, i, 6));
;;              if (conns >= 0)
;;              {
;;                      if (buf.len > 0)
;;                              appendPQExpBufferStr(&buf, "\n");
;;
;;                      if (conns == 0)
;;                              appendPQExpBuffer(&buf, _("No connections"));
;;                      else
;;                              appendPQExpBuffer(&buf, ngettext("%d connection",
;;                                                                                               "%d connections",
;;                                                                                               conns),
;;                                                                conns);
;;              }
;;
;;              attr[i] = pg_strdup(buf.data);
;;
;;              printTableAddCell(&cont, attr[i], false, false);
;;
;;              printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
;;
;;              if (verbose && pset.sversion >= 80200)
;;                      printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
;;      }
;;      termPQExpBuffer(&buf);
;;
;;      printTable(&cont, pset.queryFout, pset.logfile);
;;      printTableCleanup(&cont);
;;
;;      for (i = 0; i < nrows; i++)
;;              free(attr[i]);
;;      free(attr);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; static void
;; add_role_attribute(PQExpBuffer buf, const char *const str)
;; {
;;      if (buf->len > 0)
;;              appendPQExpBufferStr(buf, "\n");
;;
;;      appendPQExpBufferStr(buf, str);
;; }
;;
;;
;; /*
;;  * listTables()
;;  *
;;  * handler for \dt, \di, etc.
;;  *
;;  * tabtypes is an array of characters, specifying what info is desired:
;;  * t - tables
;;  * i - indexes
;;  * v - views
;;  * s - sequences
;;  * (any order of the above is fine)
;;  * If tabtypes is empty, we default to \dtvs.
;;  */
;; bool
;; listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
;; {
;;      bool            showTables = strchr(tabtypes, 't') != NULL;
;;      bool            showIndexes = strchr(tabtypes, 'i') != NULL;
;;      bool            showViews = strchr(tabtypes, 'v') != NULL;
;;      bool            showSeq = strchr(tabtypes, 's') != NULL;
;;
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;      static const bool translate_columns[] = {false, false, true, false, false, false, false};
;;
;;      if (!(showTables || showIndexes || showViews || showSeq))
;;              showTables = showViews = showSeq = true;
;;
;;      initPQExpBuffer(&buf);
;;
;;      /*
;;       * Note: as of Pg 8.2, we no longer use relkind 's', but we keep it here
;;       * for backwards compatibility.
;;       */
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT n.nspname as \"%s\",\n"
;;                                        "  c.relname as \"%s\",\n"
;;                                        "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' END as \"%s\",\n"
;;                                        "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("table"),
;;                                        gettext_noop("view"),
;;                                        gettext_noop("index"),
;;                                        gettext_noop("sequence"),
;;                                        gettext_noop("special"),
;;                                        gettext_noop("Type"),
;;                                        gettext_noop("Owner"));
;;
;;      if (showIndexes)
;;              appendPQExpBuffer(&buf,
;;                                                ",\n c2.relname as \"%s\"",
;;                                                gettext_noop("Table"));
;;
;;      if (verbose && pset.sversion >= 80100)
;;              appendPQExpBuffer(&buf,
;;                                                ",\n  pg_catalog.pg_size_pretty(pg_catalog.pg_relation_size(c.oid)) as \"%s\"",
;;                                                gettext_noop("Size"));
;;      if (verbose)
;;              appendPQExpBuffer(&buf,
;;                        ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
;;                                                gettext_noop("Description"));
;;
;;      appendPQExpBuffer(&buf,
;;                                        "\nFROM pg_catalog.pg_class c"
;;       "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
;;      if (showIndexes)
;;              appendPQExpBuffer(&buf,
;;                       "\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
;;                 "\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
;;
;;      appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (");
;;      if (showTables)
;;              appendPQExpBuffer(&buf, "'r',");
;;      if (showViews)
;;              appendPQExpBuffer(&buf, "'v',");
;;      if (showIndexes)
;;              appendPQExpBuffer(&buf, "'i',");
;;      if (showSeq)
;;              appendPQExpBuffer(&buf, "'S',");
;;      if (showSystem || pattern)
;;              appendPQExpBuffer(&buf, "'s',");                /* was RELKIND_SPECIAL in <=
;;                                                                                               * 8.1 */
;;      appendPQExpBuffer(&buf, "''");          /* dummy */
;;      appendPQExpBuffer(&buf, ")\n");
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      /*
;;       * TOAST objects are suppressed unconditionally.  Since we don't provide
;;       * any way to select relkind 't' above, we would never show toast tables
;;       * in any case; it seems a bit confusing to allow their indexes to be
;;       * shown. Use plain \d if you really need to look at a TOAST table/index.
;;       */
;;      appendPQExpBuffer(&buf, "      AND n.nspname !~ '^pg_toast'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "c.relname", NULL,
;;                                                "pg_catalog.pg_table_is_visible(c.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1,2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      if (PQntuples(res) == 0 && !pset.quiet)
;;      {
;;              if (pattern)
;;                      fprintf(pset.queryFout, _("No matching relations found.\n"));
;;              else
;;                      fprintf(pset.queryFout, _("No relations found.\n"));
;;      }
;;      else
;;      {
;;              myopt.nullPrint = NULL;
;;              myopt.title = _("List of relations");
;;              myopt.translate_header = true;
;;              myopt.translate_columns = translate_columns;
;;
;;              printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;      }
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /*
;;  * \dD
;;  *
;;  * Describes domains.
;;  */
;; bool
;; listDomains(const char *pattern, bool showSystem)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT n.nspname as \"%s\",\n"
;;                                        "       t.typname as \"%s\",\n"
;;       "       pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
;;                                        "       CASE WHEN t.typnotnull AND t.typdefault IS NOT NULL THEN 'not null default '||t.typdefault\n"
;;      "            WHEN t.typnotnull AND t.typdefault IS NULL THEN 'not null'\n"
;;                                        "            WHEN NOT t.typnotnull AND t.typdefault IS NOT NULL THEN 'default '||t.typdefault\n"
;;                                        "            ELSE ''\n"
;;                                        "       END as \"%s\",\n"
;;                                        "       pg_catalog.array_to_string(ARRAY(\n"
;;                                        "         SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid\n"
;;                                        "       ), ' ') as \"%s\"\n"
;;                                        "FROM pg_catalog.pg_type t\n"
;;         "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
;;                                        "WHERE t.typtype = 'd'\n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Type"),
;;                                        gettext_noop("Modifier"),
;;                                        gettext_noop("Check"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "t.typname", NULL,
;;                                                "pg_catalog.pg_type_is_visible(t.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of domains");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; /*
;;  * \dc
;;  *
;;  * Describes conversions.
;;  */
;; bool
;; listConversions(const char *pattern, bool showSystem)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;      static const bool translate_columns[] = {false, false, false, false, true};
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT n.nspname AS \"%s\",\n"
;;                                        "       c.conname AS \"%s\",\n"
;;         "       pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
;;              "       pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
;;                                        "       CASE WHEN c.condefault THEN '%s'\n"
;;                                        "       ELSE '%s' END AS \"%s\"\n"
;;                         "FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n\n"
;;                                        "WHERE n.oid = c.connamespace\n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Source"),
;;                                        gettext_noop("Destination"),
;;                                        gettext_noop("yes"), gettext_noop("no"),
;;                                        gettext_noop("Default?"));
;;
;;      if (!showSystem && !pattern)
;;              appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
;;                                                "      AND n.nspname <> 'information_schema'\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "c.conname", NULL,
;;                                                "pg_catalog.pg_conversion_is_visible(c.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of conversions");
;;      myopt.translate_header = true;
;;      myopt.translate_columns = translate_columns;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; /*
;;  * \dC
;;  *
;;  * Describes casts.
;;  */
;; bool
;; listCasts(const char *pattern)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;      static const bool translate_columns[] = {false, false, false, true};
;;
;;      initPQExpBuffer(&buf);
;;
;;      /*
;;       * We need a left join to pg_proc for binary casts; the others are just
;;       * paranoia.  Also note that we don't attempt to localize '(binary
;;       * coercible)', because there's too much risk of gettext translating a
;;       * function name that happens to match some string in the PO database.
;;       */
;;      printfPQExpBuffer(&buf,
;;                         "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
;;                         "       pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n"
;;                                "       CASE WHEN castfunc = 0 THEN '(binary coercible)'\n"
;;                                        "            ELSE p.proname\n"
;;                                        "       END as \"%s\",\n"
;;                                        "       CASE WHEN c.castcontext = 'e' THEN '%s'\n"
;;                                        "            WHEN c.castcontext = 'a' THEN '%s'\n"
;;                                        "            ELSE '%s'\n"
;;                                        "       END as \"%s\"\n"
;;                               "FROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
;;                                        "     ON c.castfunc = p.oid\n"
;;                                        "     LEFT JOIN pg_catalog.pg_type ts\n"
;;                                        "     ON c.castsource = ts.oid\n"
;;                                        "     LEFT JOIN pg_catalog.pg_namespace ns\n"
;;                                        "     ON ns.oid = ts.typnamespace\n"
;;                                        "     LEFT JOIN pg_catalog.pg_type tt\n"
;;                                        "     ON c.casttarget = tt.oid\n"
;;                                        "     LEFT JOIN pg_catalog.pg_namespace nt\n"
;;                                        "     ON nt.oid = tt.typnamespace\n"
;;                                        "WHERE (true",
;;                                        gettext_noop("Source type"),
;;                                        gettext_noop("Target type"),
;;                                        gettext_noop("Function"),
;;        gettext_noop("no"), gettext_noop("in assignment"), gettext_noop("yes"),
;;                                        gettext_noop("Implicit?"));
;;
;;      /*
;;       * Match name pattern against either internal or external name of either
;;       * castsource or casttarget
;;       */
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "ns.nspname", "ts.typname",
;;                                                "pg_catalog.format_type(ts.oid, NULL)",
;;                                                "pg_catalog.pg_type_is_visible(ts.oid)");
;;
;;      appendPQExpBuffer(&buf, ") OR (true");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "nt.nspname", "tt.typname",
;;                                                "pg_catalog.format_type(tt.oid, NULL)",
;;                                                "pg_catalog.pg_type_is_visible(tt.oid)");
;;
;;      appendPQExpBuffer(&buf, ")\nORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of casts");
;;      myopt.translate_header = true;
;;      myopt.translate_columns = translate_columns;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; /*
;;  * \dn
;;  *
;;  * Describes schemas (namespaces)
;;  */
;; bool
;; listSchemas(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      initPQExpBuffer(&buf);
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT n.nspname AS \"%s\",\n"
;;                                        "  pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"",
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Owner"));
;;
;;      if (verbose)
;;      {
;;              appendPQExpBuffer(&buf, ",\n  ");
;;              printACLColumn(&buf, "n.nspacl");
;;              appendPQExpBuffer(&buf,
;;                ",\n  pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
;;                                                gettext_noop("Description"));
;;      }
;;
;;      appendPQExpBuffer(&buf,
;;                                        "\nFROM pg_catalog.pg_namespace n\n"
;;                                        "WHERE        (n.nspname !~ '^pg_temp_' OR\n"
;;                 "             n.nspname = (pg_catalog.current_schemas(true))[1])\n");                /* temp schema is first */
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                NULL, "n.nspname", NULL,
;;                                                NULL);
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of schemas");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /*
;;  * \dFp
;;  * list text search parsers
;;  */
;; bool
;; listTSParsers(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      if (pset.sversion < 80300)
;;      {
;;              fprintf(stderr, _("The server (version %d.%d) does not support full text search.\n"),
;;                              pset.sversion / 10000, (pset.sversion / 100) % 100);
;;              return true;
;;      }
;;
;;      if (verbose)
;;              return listTSParsersVerbose(pattern);
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT \n"
;;                                        "  n.nspname as \"%s\",\n"
;;                                        "  p.prsname as \"%s\",\n"
;;                      "  pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
;;                                        "FROM pg_catalog.pg_ts_parser p \n"
;;                 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Description")
;;              );
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                "n.nspname", "p.prsname", NULL,
;;                                                "pg_catalog.pg_ts_parser_is_visible(p.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of text search parsers");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; /*
;;  * full description of parsers
;;  */
;; static bool
;; listTSParsersVerbose(const char *pattern)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      int                     i;
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT p.oid, \n"
;;                                        "  n.nspname, \n"
;;                                        "  p.prsname \n"
;;                                        "FROM pg_catalog.pg_ts_parser p\n"
;;                      "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
;;              );
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                "n.nspname", "p.prsname", NULL,
;;                                                "pg_catalog.pg_ts_parser_is_visible(p.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      if (PQntuples(res) == 0)
;;      {
;;              if (!pset.quiet)
;;                      fprintf(stderr, _("Did not find any text search parser named \"%s\".\n"),
;;                                      pattern);
;;              PQclear(res);
;;              return false;
;;      }
;;
;;      for (i = 0; i < PQntuples(res); i++)
;;      {
;;              const char *oid;
;;              const char *nspname = NULL;
;;              const char *prsname;
;;
;;              oid = PQgetvalue(res, i, 0);
;;              if (!PQgetisnull(res, i, 1))
;;                      nspname = PQgetvalue(res, i, 1);
;;              prsname = PQgetvalue(res, i, 2);
;;
;;              if (!describeOneTSParser(oid, nspname, prsname))
;;              {
;;                      PQclear(res);
;;                      return false;
;;              }
;;
;;              if (cancel_pressed)
;;              {
;;                      PQclear(res);
;;                      return false;
;;              }
;;      }
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; static bool
;; describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      char            title[1024];
;;      printQueryOpt myopt = pset.popt;
;;      static const bool translate_columns[] = {true, false, false};
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT '%s' AS \"%s\", \n"
;;                                        "   p.prsstart::pg_catalog.regproc AS \"%s\", \n"
;;                "   pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\" \n"
;;                                        " FROM pg_catalog.pg_ts_parser p \n"
;;                                        " WHERE p.oid = '%s' \n"
;;                                        "UNION ALL \n"
;;                                        "SELECT '%s', \n"
;;                                        "   p.prstoken::pg_catalog.regproc, \n"
;;                                      "   pg_catalog.obj_description(p.prstoken, 'pg_proc') \n"
;;                                        " FROM pg_catalog.pg_ts_parser p \n"
;;                                        " WHERE p.oid = '%s' \n"
;;                                        "UNION ALL \n"
;;                                        "SELECT '%s', \n"
;;                                        "   p.prsend::pg_catalog.regproc, \n"
;;                                        "   pg_catalog.obj_description(p.prsend, 'pg_proc') \n"
;;                                        " FROM pg_catalog.pg_ts_parser p \n"
;;                                        " WHERE p.oid = '%s' \n"
;;                                        "UNION ALL \n"
;;                                        "SELECT '%s', \n"
;;                                        "   p.prsheadline::pg_catalog.regproc, \n"
;;                               "   pg_catalog.obj_description(p.prsheadline, 'pg_proc') \n"
;;                                        " FROM pg_catalog.pg_ts_parser p \n"
;;                                        " WHERE p.oid = '%s' \n"
;;                                        "UNION ALL \n"
;;                                        "SELECT '%s', \n"
;;                                        "   p.prslextype::pg_catalog.regproc, \n"
;;                                "   pg_catalog.obj_description(p.prslextype, 'pg_proc') \n"
;;                                        " FROM pg_catalog.pg_ts_parser p \n"
;;                                        " WHERE p.oid = '%s' \n",
;;                                        gettext_noop("Start parse"),
;;                                        gettext_noop("Method"),
;;                                        gettext_noop("Function"),
;;                                        gettext_noop("Description"),
;;                                        oid,
;;                                        gettext_noop("Get next token"),
;;                                        oid,
;;                                        gettext_noop("End parse"),
;;                                        oid,
;;                                        gettext_noop("Get headline"),
;;                                        oid,
;;                                        gettext_noop("Get token types"),
;;                                        oid);
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      if (nspname)
;;              sprintf(title, _("Text search parser \"%s.%s\""), nspname, prsname);
;;      else
;;              sprintf(title, _("Text search parser \"%s\""), prsname);
;;      myopt.title = title;
;;      myopt.footers = NULL;
;;      myopt.default_footer = false;
;;      myopt.translate_header = true;
;;      myopt.translate_columns = translate_columns;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT t.alias as \"%s\", \n"
;;                                        "  t.description as \"%s\" \n"
;;                        "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t \n"
;;                                        "ORDER BY 1;",
;;                                        gettext_noop("Token name"),
;;                                        gettext_noop("Description"),
;;                                        oid);
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      if (nspname)
;;              sprintf(title, _("Token types for parser \"%s.%s\""), nspname, prsname);
;;      else
;;              sprintf(title, _("Token types for parser \"%s\""), prsname);
;;      myopt.title = title;
;;      myopt.footers = NULL;
;;      myopt.default_footer = true;
;;      myopt.translate_header = true;
;;      myopt.translate_columns = NULL;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /*
;;  * \dFd
;;  * list text search dictionaries
;;  */
;; bool
;; listTSDictionaries(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      if (pset.sversion < 80300)
;;      {
;;              fprintf(stderr, _("The server (version %d.%d) does not support full text search.\n"),
;;                              pset.sversion / 10000, (pset.sversion / 100) % 100);
;;              return true;
;;      }
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT \n"
;;                                        "  n.nspname as \"%s\",\n"
;;                                        "  d.dictname as \"%s\",\n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"));
;;
;;      if (verbose)
;;      {
;;              appendPQExpBuffer(&buf,
;;                                                "  ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM \n"
;;                                                "    pg_catalog.pg_ts_template t \n"
;;                                                "                      LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace \n"
;;                                                "                      WHERE d.dicttemplate = t.oid ) AS  \"%s\", \n"
;;                                                "  d.dictinitoption as \"%s\", \n",
;;                                                gettext_noop("Template"),
;;                                                gettext_noop("Init options"));
;;      }
;;
;;      appendPQExpBuffer(&buf,
;;                       "  pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
;;                                        gettext_noop("Description"));
;;
;;      appendPQExpBuffer(&buf, "FROM pg_catalog.pg_ts_dict d\n"
;;               "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                "n.nspname", "d.dictname", NULL,
;;                                                "pg_catalog.pg_ts_dict_is_visible(d.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of text search dictionaries");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /*
;;  * \dFt
;;  * list text search templates
;;  */
;; bool
;; listTSTemplates(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      if (pset.sversion < 80300)
;;      {
;;              fprintf(stderr, _("The server (version %d.%d) does not support full text search.\n"),
;;                              pset.sversion / 10000, (pset.sversion / 100) % 100);
;;              return true;
;;      }
;;
;;      initPQExpBuffer(&buf);
;;
;;      if (verbose)
;;              printfPQExpBuffer(&buf,
;;                                                "SELECT \n"
;;                                                "  n.nspname AS \"%s\",\n"
;;                                                "  t.tmplname AS \"%s\",\n"
;;                                                "  t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
;;                                                "  t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
;;               "  pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
;;                                                gettext_noop("Schema"),
;;                                                gettext_noop("Name"),
;;                                                gettext_noop("Init"),
;;                                                gettext_noop("Lexize"),
;;                                                gettext_noop("Description"));
;;      else
;;              printfPQExpBuffer(&buf,
;;                                                "SELECT \n"
;;                                                "  n.nspname AS \"%s\",\n"
;;                                                "  t.tmplname AS \"%s\",\n"
;;               "  pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
;;                                                gettext_noop("Schema"),
;;                                                gettext_noop("Name"),
;;                                                gettext_noop("Description"));
;;
;;      appendPQExpBuffer(&buf, "FROM pg_catalog.pg_ts_template t\n"
;;               "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                "n.nspname", "t.tmplname", NULL,
;;                                                "pg_catalog.pg_ts_template_is_visible(t.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of text search templates");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /*
;;  * \dF
;;  * list text search configurations
;;  */
;; bool
;; listTSConfigs(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      if (pset.sversion < 80300)
;;      {
;;              fprintf(stderr, _("The server (version %d.%d) does not support full text search.\n"),
;;                              pset.sversion / 10000, (pset.sversion / 100) % 100);
;;              return true;
;;      }
;;
;;      if (verbose)
;;              return listTSConfigsVerbose(pattern);
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT \n"
;;                                        "   n.nspname as \"%s\",\n"
;;                                        "   c.cfgname as \"%s\",\n"
;;                 "   pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
;;                                        "FROM pg_catalog.pg_ts_config c\n"
;;                "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace \n",
;;                                        gettext_noop("Schema"),
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Description")
;;              );
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                "n.nspname", "c.cfgname", NULL,
;;                                                "pg_catalog.pg_ts_config_is_visible(c.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of text search configurations");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; static bool
;; listTSConfigsVerbose(const char *pattern)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      int                     i;
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT c.oid, c.cfgname,\n"
;;                                        "   n.nspname, \n"
;;                                        "   p.prsname, \n"
;;                                        "   np.nspname as pnspname \n"
;;                                        "FROM pg_catalog.pg_ts_config c \n"
;;         "   LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace, \n"
;;                                        " pg_catalog.pg_ts_parser p \n"
;;        "   LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace \n"
;;                                        "WHERE  p.oid = c.cfgparser\n"
;;              );
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, true, false,
;;                                                "n.nspname", "c.cfgname", NULL,
;;                                                "pg_catalog.pg_ts_config_is_visible(c.oid)");
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 3, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      if (PQntuples(res) == 0)
;;      {
;;              if (!pset.quiet)
;;                      fprintf(stderr, _("Did not find any text search configuration named \"%s\".\n"),
;;                                      pattern);
;;              PQclear(res);
;;              return false;
;;      }
;;
;;      for (i = 0; i < PQntuples(res); i++)
;;      {
;;              const char *oid;
;;              const char *cfgname;
;;              const char *nspname = NULL;
;;              const char *prsname;
;;              const char *pnspname = NULL;
;;
;;              oid = PQgetvalue(res, i, 0);
;;              cfgname = PQgetvalue(res, i, 1);
;;              if (!PQgetisnull(res, i, 2))
;;                      nspname = PQgetvalue(res, i, 2);
;;              prsname = PQgetvalue(res, i, 3);
;;              if (!PQgetisnull(res, i, 4))
;;                      pnspname = PQgetvalue(res, i, 4);
;;
;;              if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
;;              {
;;                      PQclear(res);
;;                      return false;
;;              }
;;
;;              if (cancel_pressed)
;;              {
;;                      PQclear(res);
;;                      return false;
;;              }
;;      }
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; static bool
;; describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
;;                                      const char *pnspname, const char *prsname)
;; {
;;      PQExpBufferData buf,
;;                              title;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      initPQExpBuffer(&buf);
;;
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT \n"
;;                                        "  ( SELECT t.alias FROM \n"
;;                                        "    pg_catalog.ts_token_type(c.cfgparser) AS t \n"
;;                                        "    WHERE t.tokid = m.maptokentype ) AS \"%s\", \n"
;;                                        "  pg_catalog.btrim( \n"
;;                                "    ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary \n"
;;                                        "           FROM pg_catalog.pg_ts_config_map AS mm \n"
;;                                        "           WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype \n"
;;                                        "           ORDER BY mapcfg, maptokentype, mapseqno \n"
;;                                        "    ) :: pg_catalog.text , \n"
;;                                        "  '{}') AS \"%s\" \n"
;;       "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m \n"
;;                                        "WHERE c.oid = '%s' AND m.mapcfg = c.oid \n"
;;                                        "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser \n"
;;                                        "ORDER BY 1",
;;                                        gettext_noop("Token"),
;;                                        gettext_noop("Dictionaries"),
;;                                        oid);
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      initPQExpBuffer(&title);
;;
;;      if (nspname)
;;              appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
;;                                                nspname, cfgname);
;;      else
;;              appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
;;                                                cfgname);
;;
;;      if (pnspname)
;;              appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
;;                                                pnspname, prsname);
;;      else
;;              appendPQExpBuffer(&title, _("\nParser: \"%s\""),
;;                                                prsname);
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = title.data;
;;      myopt.footers = NULL;
;;      myopt.default_footer = false;
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      termPQExpBuffer(&title);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;;
;; /*
;;  * \dew
;;  *
;;  * Describes foreign-data wrappers
;;  */
;; bool
;; listForeignDataWrappers(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      if (pset.sversion < 80400)
;;      {
;;              fprintf(stderr, _("The server (version %d.%d) does not support foreign-data wrappers.\n"),
;;                              pset.sversion / 10000, (pset.sversion / 100) % 100);
;;              return true;
;;      }
;;
;;      initPQExpBuffer(&buf);
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT fdwname AS \"%s\",\n"
;;                                        "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n"
;;                                        "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Owner"),
;;                                        gettext_noop("Validator"));
;;
;;      if (verbose)
;;      {
;;              appendPQExpBuffer(&buf, ",\n  ");
;;              printACLColumn(&buf, "fdwacl");
;;              appendPQExpBuffer(&buf,
;;                                                ",\n  fdwoptions AS \"%s\"",
;;                                                gettext_noop("Options"));
;;      }
;;
;;      appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                NULL, "fdwname", NULL, NULL);
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of foreign-data wrappers");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; /*
;;  * \des
;;  *
;;  * Describes foreign servers.
;;  */
;; bool
;; listForeignServers(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      if (pset.sversion < 80400)
;;      {
;;              fprintf(stderr, _("The server (version %d.%d) does not support foreign servers.\n"),
;;                              pset.sversion / 10000, (pset.sversion / 100) % 100);
;;              return true;
;;      }
;;
;;      initPQExpBuffer(&buf);
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT s.srvname AS \"%s\",\n"
;;                                        "  pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
;;                                        "  f.fdwname AS \"%s\"",
;;                                        gettext_noop("Name"),
;;                                        gettext_noop("Owner"),
;;                                        gettext_noop("Foreign-data wrapper"));
;;
;;      if (verbose)
;;      {
;;              appendPQExpBuffer(&buf, ",\n  ");
;;              printACLColumn(&buf, "s.srvacl");
;;              appendPQExpBuffer(&buf,
;;                                                ",\n"
;;                                                "  s.srvtype AS \"%s\",\n"
;;                                                "  s.srvversion AS \"%s\",\n"
;;                                                "  s.srvoptions AS \"%s\"",
;;                                                gettext_noop("Type"),
;;                                                gettext_noop("Version"),
;;                                                gettext_noop("Options"));
;;      }
;;
;;      appendPQExpBuffer(&buf,
;;                                        "\nFROM pg_catalog.pg_foreign_server s\n"
;;         "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                NULL, "s.srvname", NULL, NULL);
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of foreign servers");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; /*
;;  * \deu
;;  *
;;  * Describes user mappings.
;;  */
;; bool
;; listUserMappings(const char *pattern, bool verbose)
;; {
;;      PQExpBufferData buf;
;;      PGresult   *res;
;;      printQueryOpt myopt = pset.popt;
;;
;;      if (pset.sversion < 80400)
;;      {
;;              fprintf(stderr, _("The server (version %d.%d) does not support user mappings.\n"),
;;                              pset.sversion / 10000, (pset.sversion / 100) % 100);
;;              return true;
;;      }
;;
;;      initPQExpBuffer(&buf);
;;      printfPQExpBuffer(&buf,
;;                                        "SELECT um.srvname AS \"%s\",\n"
;;                                        "  um.usename AS \"%s\"",
;;                                        gettext_noop("Server"),
;;                                        gettext_noop("User name"));
;;
;;      if (verbose)
;;              appendPQExpBuffer(&buf,
;;                                                ",\n  um.umoptions AS \"%s\"",
;;                                                gettext_noop("Options"));
;;
;;      appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
;;
;;      processSQLNamePattern(pset.db, &buf, pattern, false, false,
;;                                                NULL, "um.srvname", "um.usename", NULL);
;;
;;      appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
;;
;;      res = PSQLexec(buf.data, false);
;;      termPQExpBuffer(&buf);
;;      if (!res)
;;              return false;
;;
;;      myopt.nullPrint = NULL;
;;      myopt.title = _("List of user mappings");
;;      myopt.translate_header = true;
;;
;;      printQuery(res, &myopt, pset.queryFout, pset.logfile);
;;
;;      PQclear(res);
;;      return true;
;; }
;;
;; /*
;;  * printACLColumn
;;  *
;;  * Helper function for consistently formatting ACL (privilege) columns.
;;  * The proper targetlist entry is appended to buf.   Note lack of any
;;  * whitespace or comma decoration.
;;  */
;; static void
;; printACLColumn(PQExpBuffer buf, const char *colname)
;; {
;;      if (pset.sversion >= 80100)
;;              appendPQExpBuffer(buf,
;;                                                "pg_catalog.array_to_string(%s, E'\\n') AS \"%s\"",
;;                                                colname, gettext_noop("Access privileges"));
;;      else
;;              appendPQExpBuffer(buf,
;;                                                "pg_catalog.array_to_string(%s, '\\n') AS \"%s\"",
;;                                                colname, gettext_noop("Access privileges"));
;; }



;;;; THE END ;;;;
ViewGit