home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / modes / sql-mode.el < prev    next >
Encoding:
Text File  |  1992-11-26  |  33.0 KB  |  856 lines

  1. ;; sql-mode.el - Oracle SQL*Plus interface
  2. ;;
  3. ;; Author:  Jim Lange, Oracle Corporation
  4. ;; Date:    27-MAR-90
  5. ;; Bugs to: jlange@us.oracle.com
  6. ;;
  7. ;; $Header: /home/jlange/emacs/RCS/sql-mode.el,v 1.2 90/04/10 07:11:42 jlange Exp Locker: jlange $
  8. ;; 
  9. ;; Revision History:
  10. ;;   19-APR-90 (jlange) - Trap EXIT/QUIT and terminate session.
  11. ;;                      - Trap EDIT, but just print message.
  12. ;;                      - Allow multiple sqlplus sessions by renaming current
  13. ;;                        buffer to new name and executing sqlplus again.
  14. ;;                      - Set left-margin to 5 in sqlplus-mode so that
  15. ;;                        newline-and-indent (C-j) will indent.
  16. ;;                      - Added (accept-process-output process) in 
  17. ;;                        sqlplus-kill-command to prevent occasional scrambled
  18. ;;                        lines when command is executed.
  19. ;;                      - Added sqlplus-reset-buffer.
  20. ;;   25-APR-90 (jlange) - Treat GET like LIST and RUN--ignore in 
  21. ;;                        sqlplus-get-command.
  22. ;;                      - Add sqlplus-drop-old-lines.
  23. ;;   04-MAY-90 (jlange) - Add sqlplus-copy-word (C-c C-w).
  24. ;;                      - Enhance sqlplus-kill-command to delete command or
  25. ;;                        command output, depending on location of point.
  26. ;;   11-MAY-90 (jlange) - In sql-send-region, detect imbedded SQL statement 
  27. ;;                        format (used in SQL*FORMS, SQL*REPORT, PRO*C, etc.) 
  28. ;;                        and convert to standard SQL before executing (remove
  29. ;;                        INTO clause and convert :WORD to &WORD).
  30. ;;                      - Automatically load and save session history based
  31. ;;                        on the variable sqlplus-keep-history.
  32. ;;   05-JUN-90 (jlange) - Delete ~/sqlplus.buf when exiting.
  33. ;;                      - In sqlplus-send-region, when performing substitutions
  34. ;;                        in statements, add "/" at end if not present.
  35. ;;   12-JUN-90 (jlange) - In sqlplus-send-region, look for :text.text and convert
  36. ;;                        to &text_text (used in SQL*Forms for block.field).
  37. ;;   07-SEP-90 (jlange) - Removed process argument from accept-process-output in
  38. ;;                        sql-send-line to prevent apparent lockup after issuing
  39. ;;                        complex SQL statement.
  40. ;;                      - Trap EDIT command and open new emacs buffer containing
  41. ;;                        command text.  (new revision 1.2)
  42. ;;   08-MAY-91 (jlange) - In sql-send-region, truncate &-variables to 30 characters
  43. ;;                        before executing.
  44. ;;---------------------------------------------------------------------------------
  45.  
  46. ;; Copyright (C) 1990 Free Software Foundation, Inc., and Jim Lange.
  47. ;;
  48. ;; This file is part of GNU Emacs.  It is derived from 18.55's shell.el.
  49. ;;
  50. ;; GNU Emacs is distributed in the hope that it will be useful,
  51. ;; but WITHOUT ANY WARRANTY.  No author or distributor
  52. ;; accepts responsibility to anyone for the consequences of using it
  53. ;; or for whether it serves any particular purpose or works at all,
  54. ;; unless he says so in writing.  Refer to the GNU Emacs General Public
  55. ;; License for full details.
  56. ;;
  57. ;; Everyone is granted permission to copy, modify and redistribute
  58. ;; GNU Emacs, but only under the conditions described in the
  59. ;; GNU Emacs General Public License.   A copy of this license is
  60. ;; supposed to have been given to you along with GNU Emacs so you
  61. ;; can know your rights and responsibilities.  It should be in a
  62. ;; file named COPYING.  Among other things, the copyright notice
  63. ;; and this notice must be preserved on all copies.
  64.  
  65. ;;----------------------------------------------------------------------
  66.  
  67. ;; This file contains function definitions for two new Emacs major modes,
  68. ;; sql-mode and sqlplus-mode.  Sql-mode is for editing SQL statements in
  69. ;; a standard text buffer.  Sqlplus-mode is used in conjunction with the 
  70. ;; sqlplus function which runs SQL*Plus as an inferior process (similar to 
  71. ;; shell-mode).
  72. ;;
  73. ;; Sql-mode is provided primarily as a convenience so that SQL statements 
  74. ;; may be sent to SQL*Plus running in another buffer.  Eventually it may
  75. ;; also provide automatic formatting of SQL statements based on Oracle 
  76. ;; indentation standards (if they exist).
  77. ;;
  78. ;; Both modes turn on abbrev-mode and share a mode-specific abbreviation table
  79. ;; with some predefined abbreviations.  Users may add to these or load in their
  80. ;; own.  Abbrev-mode may be turned off in a user defined hook routine.
  81. ;;
  82. ;; The following commands should be added to a global init file or to any 
  83. ;; user's .emacs file to conveniently use the new sql modes.
  84. ;;
  85. ;;      (autoload 'sqlplus "sql-mode" 
  86. ;;        "Run an interactive SQL*plus session in a separate buffer." t)
  87. ;;
  88. ;;      (autoload 'sql-mode "sql-mode"
  89. ;;        "Major mode for editing SQL*plus batch files." t)
  90. ;;
  91. ;;      (setq auto-mode-alist (cons '("\\.sql$" . sql-mode) auto-mode-alist))
  92. ;;
  93. ;; Possible Enhancements:
  94. ;;   - Detect actual prompt string and set sql-prompt variable appropriately.
  95. ;;
  96. ;;   - Suggestions?
  97.  
  98. (provide 'sql-mode)
  99.  
  100. ;; define variables
  101. (defvar sqlplus-startup-message
  102.   (concat 
  103.    "Emacs SQL*Plus Interpreter:  by Jim Lange of Oracle Corporation\n"
  104.    (substring "$Revision: 1.2 $" 1 -1)
  105.    "\n\nCopyright (c) 1990 Free Software Foundation, Inc., and Jim Lange.\n"
  106.    "------------------------------")
  107.   "Message displayed when \\[sqlplus] is executed.")
  108.  
  109. (defvar last-output-start nil
  110.   "In a sqlplus-mode buffer, marker for beginning of last batch of output.")
  111. (defvar sql-prompt nil
  112.   "In a sqlplus-mode buffer, string containing prompt text.")
  113. (defvar sql-continue-pattern nil
  114.   "In a sqlplus-mode buffer, regular expression for continuation line prompt.")
  115. (defvar sqlplus-username-password nil
  116.   "The username/password to use when starting SQL*Plus.")
  117. (defvar sqlplus-stack-pointer 0
  118.   "Current command recalled from history of commands.")
  119. (defvar sqlplus-keep-history nil
  120.   "If non-nil, save current session in file .sqlhist when exiting.")
  121. (defvar sqlplus-lines-to-keep 1000
  122.   "Number of lines to keep in a SQL*Plus buffer when \\[sqlplus-drop-old-lines] is executed.")
  123. (defvar sqlplus-mode-map nil)
  124. (defvar sql-mode-map nil)
  125. (defvar sql-mode-syntax-table nil
  126.   "Syntax table used while in SQL and SQL*Plus modes.")
  127. (defvar sql-mode-abbrev-table nil
  128.   "Abbrev table used in SQL and SQL*Plus modes.")
  129.  
  130. ;; initialize syntax table
  131. (if sql-mode-syntax-table
  132.     ()
  133.   (setq sql-mode-syntax-table (make-syntax-table))
  134.   (modify-syntax-entry ?/ ". 14" sql-mode-syntax-table)   ; comment start
  135.   (modify-syntax-entry ?* ". 23" sql-mode-syntax-table)
  136.   (modify-syntax-entry ?+ "." sql-mode-syntax-table)
  137.   (modify-syntax-entry ?- "." sql-mode-syntax-table)
  138.   (modify-syntax-entry ?= "." sql-mode-syntax-table)
  139.   (modify-syntax-entry ?% "w" sql-mode-syntax-table)
  140.   (modify-syntax-entry ?< "." sql-mode-syntax-table)
  141.   (modify-syntax-entry ?> "." sql-mode-syntax-table)
  142.   (modify-syntax-entry ?& "w" sql-mode-syntax-table)
  143.   (modify-syntax-entry ?| "." sql-mode-syntax-table)
  144.   (modify-syntax-entry ?_ "w" sql-mode-syntax-table)     ; make _ part of words
  145.   (modify-syntax-entry ?\' "\"" sql-mode-syntax-table))
  146.  
  147. ;; initialize abbreviations
  148. (if sql-mode-abbrev-table
  149.     nil
  150.   (define-abbrev-table 'sql-mode-abbrev-table ())
  151.   (let ((abbrevs-changed nil))
  152.     (define-abbrev sql-mode-abbrev-table  "d"   "describe" nil)
  153.     (define-abbrev sql-mode-abbrev-table  "s"   "select"   nil)
  154.     (define-abbrev sql-mode-abbrev-table  "f"   "from"     nil)
  155.     (define-abbrev sql-mode-abbrev-table  "w"   "where"    nil)
  156.     (define-abbrev sql-mode-abbrev-table  "o"   "order by" nil)
  157.     (define-abbrev sql-mode-abbrev-table  "l"   "like"     nil)
  158.     (define-abbrev sql-mode-abbrev-table  "i"   "in ("     nil)
  159.     (define-abbrev sql-mode-abbrev-table  "g"   "group by" nil)
  160.     (define-abbrev sql-mode-abbrev-table  "h"   "having"   nil)
  161.     (define-abbrev sql-mode-abbrev-table  "n"   "not"      nil)
  162.   )
  163. )
  164.  
  165. ;;-----------------------------
  166.  
  167. (defun sql-mode ()
  168.   "Major mode for editing SQL*Plus batch files.
  169. \\{sql-mode-map}
  170. sql-send-buffer and sql-send-region are commands that will send SQL*Plus
  171. commands defined in the current buffer to SQL*Plus to be executed.  Output
  172. is displayed in the *sqlplus* buffer (which will open as separate window
  173. if it does not already exist).  (use '\\[describe-mode]' while in *sqlplus*
  174. buffer for information on sqlplus-mode.)
  175.  
  176. Entry to this mode calls the value of sqlplus-mode-hook with no args,
  177. if that value is non-nil.  Abbrev-mode is also enabled with the following
  178. abbreviations available by default:
  179.  
  180.         s  ->  Select
  181.         f  ->  From
  182.         w  ->  Where
  183.         o  ->  Order By
  184.  
  185. Use \\[list-abbrevs] for a full list.
  186.  
  187. If the SQL statements to be executed contain variables prefixed with colons
  188. or INTO clauses, the colons are converted into ampersands and the INTO clauses
  189. are removed before being sent to SQL*Plus.  This provides compatibility with
  190. Pro*C, SQL*Report, and SQL*Forms (.inp files).  For example,
  191.  
  192.      SELECT SYSDATE + :days_added INTO :variable FROM SYSTEM.DUAL
  193.  
  194. is converted to
  195.  
  196.      SELECT SYSDATE + &days_added FROM SYSTEM.DUAL
  197.  
  198. and the user is prompted to enter the value of days_added."
  199.  
  200.   (interactive)
  201.   (setq major-mode 'sql-mode)
  202.   (setq mode-name "SQL")
  203.   (use-local-map sql-mode-map)
  204.   (set-syntax-table sql-mode-syntax-table)
  205.   (setq local-abbrev-table sql-mode-abbrev-table)
  206.   (abbrev-mode 1)
  207.   (setq abbrev-all-caps 1)
  208.   (setq require-final-newline t)
  209.   (run-hooks 'sql-mode-hook)
  210. )
  211.  
  212. (if sql-mode-map
  213.     nil
  214.   (setq sql-mode-map (make-sparse-keymap))
  215.   (define-key sql-mode-map "\C-c\C-x" 'sql-send-buffer)
  216.   (define-key sql-mode-map "\C-c\C-r" 'sql-send-region)
  217. )
  218.  
  219. (defun sqlplus-mode ()
  220.   "Major mode for interacting with Oracle SQL*Plus.
  221. Return at end of buffer sends line as input.
  222. Return not at end copies SQL statement to end and executes it.  
  223. \\{sqlplus-mode-map}
  224. This mode is normally invoked by 'M-x sqlplus' (not 'M-x sqlplus-mode').
  225. You will be prompted to enter a username/password combination
  226. to access the Oracle database.  This can be prevented by setting the 
  227. variable sqlplus-username-password in your .emacs file as follows:
  228.  
  229.      (setq sqlplus-username-password \"myname/mypassword\")
  230.  
  231. There are two ways of editing and re-executing prior commands.
  232. '\\[sqlplus-back-command]' and '\\[sqlplus-forward-command]' will move to the location 
  233. in the buffer of the previous or next SQL statement, respectively
  234. (based on the command prompt).  The command can then be edited
  235. normally and re-executed by pressing Return.  To insert a newline,
  236. you may press '\\[newline-and-indent]'.  '\\[sqlplus-next-command]' and '\\[sqlplus-previous-command]' 
  237. are similar except the next or previous SQL statement is inserted at
  238. the end of the buffer.  Repeating these commands will clear the
  239. current statement and recall the next or previous statement from the
  240. stack.  For additional information on command execution use 
  241. '\\[describe-key] RTN'.
  242.  
  243. '\\[show-sqlplus-output]' will move to the beginning of the last ouput
  244. generated by SQL*plus.  This is useful for reviewing the results of 
  245. the last statement.  '\\[sqlplus-end-of-buffer]' moves to the end of the buffer, 
  246. but unlike '\\[end-of-buffer]' (end-of-buffer), it does not set the mark.
  247.  
  248. '\\[sqlplus-kill-command]' deletes either the current command being entered or
  249. the last output generated by SQL*Plus, depending on when it is used.  
  250. If executed while the cursor is within a SQL statement, the statement and 
  251. any text after it are deleted.  If the cursor is within or at the end of
  252. output generated by SQL*Plus, the output is deleted and the cursor is 
  253. positioned at the end of the SQL statement that generated the ouput.  
  254. '\\[sqlplus-kill-command]' can be used like an undo command to alternately
  255. delete commands and output from the end of the buffer.
  256.  
  257. The commands sql-send-region and sql-send-buffer can be executed from
  258. another buffer to execute the SQL statements defined by the current
  259. region or entire buffer, respectively.  Output from these commands is
  260. displayed in the *sqlplus* buffer.  The major mode called sql-mode has
  261. these functions bound to key sequences.
  262.  
  263. Entry to this mode calls the value of sqlplus-mode-hook with no args,
  264. if that value is non-nil.  Abbrev-mode is also enabled with the following
  265. abbreviations available by default:
  266.  
  267.         s  ->  Select
  268.         f  ->  From
  269.         w  ->  Where
  270.         o  ->  Order By
  271.  
  272. Use '\\[list-abbrevs]' for a full list.  
  273.  
  274. If the variable sqlplus-keep-history is non-nil, the current session is
  275. saved in the file ~/.sqlhist and recalled automatically the next time
  276. sqlplus is executed.  The session will only be saved if the EXIT or QUIT
  277. command is used to terminate the SQL*Plus session.  The maximum number of 
  278. lines saved can be set with the variable sqlplus-lines-to-keep which defaults
  279. to 1000.  The command '\\[sqlplus-drop-old-lines]' will truncate the buffer 
  280. to this length at any time.  sqlplus-keep-history and sqlplus-lines-to-keep
  281. should be set in your .emacs file.
  282.  
  283. If the *sqlplus* buffer is killed with '\\[kill-buffer]', the SQL*Plus
  284. process will automatically be terminated, but the session will not be saved,
  285. even if sqlplus-keep-history is non-nil.
  286.  
  287. '\\[sqlplus-reset-buffer]' will delete all ouput lines from the buffer, leaving
  288. only commands.  This will significantly shrink the buffer, but retain a full 
  289. history of commands for re-execution."
  290.  
  291.   (interactive)
  292.   (kill-all-local-variables)
  293.   (setq major-mode 'sqlplus-mode)
  294.   (setq mode-name "SQL*Plus")
  295.   (setq mode-line-process '(": %s"))
  296.   (use-local-map sqlplus-mode-map)
  297.   (set-syntax-table sql-mode-syntax-table)
  298.   (setq local-abbrev-table sql-mode-abbrev-table)
  299.   (make-local-variable 'last-output-start)
  300.   (setq last-output-start (make-marker))
  301.   (make-local-variable 'sql-prompt)
  302.   (setq sql-prompt "^\\(SQL> \\)+")     ;* allows "SQL> SQL> ..."
  303.   (make-local-variable 'sql-continue-pattern)
  304.   (setq sql-continue-pattern "^[ 0-9][ 0-9][0-9][* \t][ \t]\\|     ")
  305.   (setq indent-tabs-mode nil)
  306.   (setq left-margin 5)
  307.   (abbrev-mode 1)
  308.   (setq abbrev-all-caps 1)
  309.   (run-hooks 'sqlplus-mode-hook)
  310. )
  311.  
  312. (if sqlplus-mode-map
  313.     nil
  314.   (setq sqlplus-mode-map (make-sparse-keymap))
  315.   (define-key sqlplus-mode-map "\C-m" 'sqlplus-execute-command)      
  316.   (define-key sqlplus-mode-map "\t" 'indent-relative)                
  317.   (define-key sqlplus-mode-map "\C-c\C-c" 'interrupt-sqlplus-subjob) 
  318.   (define-key sqlplus-mode-map "\C-c\C-r" 'show-sqlplus-output)      
  319.   (define-key sqlplus-mode-map "\C-c\C-p" 'sqlplus-previous-command) 
  320.   (define-key sqlplus-mode-map "\C-c\C-n" 'sqlplus-next-command)     
  321.   (define-key sqlplus-mode-map "\C-c\C-e" 'sqlplus-end-of-buffer)    
  322.   (define-key sqlplus-mode-map "\C-c\C-b" 'sqlplus-back-command)     
  323.   (define-key sqlplus-mode-map "\C-c\C-f" 'sqlplus-forward-command)  
  324.   (define-key sqlplus-mode-map "\C-c\C-k" 'sqlplus-kill-command)     
  325.   (define-key sqlplus-mode-map "\C-c\C-x" 'sqlplus-reset-buffer)     
  326.   (define-key sqlplus-mode-map "\C-c\C-d" 'sqlplus-drop-old-lines)   
  327.   (define-key sqlplus-mode-map "\C-c\C-w" 'sqlplus-copy-word)        
  328.   (define-key sqlplus-mode-map "\C-c\C-s" 'sqlplus-save-session)     
  329. )
  330.  
  331. ;;-----------------------------
  332.  
  333. (defun sqlplus ()
  334.   "Start up an interactive SQL*Plus session in a new buffer.
  335. If an active SQL*Plus process already exists, will switch to that
  336. buffer."
  337.   (interactive)
  338.   (let ((process (sqlplus-start)))
  339.     (switch-to-buffer "*sqlplus*")
  340.     (if (and sqlplus-keep-history
  341.          (file-readable-p (expand-file-name "~/.sqlhist")))
  342.     (progn
  343.       (sit-for 1)
  344.       (while (accept-process-output) (sleep-for 1)) 
  345.       (insert-file-contents (expand-file-name "~/.sqlhist") nil)
  346.       (goto-char (point-max))
  347.       (set-marker (process-mark process) (point))
  348.       (message ".sqlhist loaded")
  349.         )
  350.     );endif
  351.   )
  352. )
  353.  
  354. (defun sqlplus-start ()
  355.   "Start up an interactive SQL*Plus session in a new buffer."
  356.   (let ((sqlplus-buffer (get-buffer-create "*sqlplus*")) process)
  357.     (setq process            ; set process
  358.       (or                ; to the first that is non-nil:
  359.        (get-buffer-process sqlplus-buffer)    ; current process
  360.        (progn                      ; or new process
  361.          (set-buffer sqlplus-buffer)
  362.          (insert sqlplus-startup-message)
  363.          (start-process "SQL*plus" sqlplus-buffer "sqlplus" 
  364.         (or sqlplus-username-password
  365.             (setq sqlplus-username-password 
  366.               (read-string "Enter SQL*plus username/password: "))))
  367.        ))
  368.     )
  369.     (set-buffer sqlplus-buffer)
  370.     (goto-char (point-max))
  371.     (set-marker (process-mark process) (point))
  372.     (sqlplus-mode)
  373.     process             ; return process
  374.   )
  375. )
  376.  
  377. (defun sqlplus-execute-command ()
  378. "When executed at end of buffer, sends text entered since last 
  379. output from SQL*Plus.  When executed while positioned within another
  380. valid command in the buffer, copies command to end of buffer and 
  381. re-executes it.  If point is within a multi-line statement at the end
  382. of the buffer (such as after '\\[sqlplus-previous-command]'), the entire
  383. statement will be cleared and re-entered one line at a time.
  384.  
  385. Multi-line statements are recognized by the continuation prompt displayed
  386. by SQL*Plus.  This is controlled by the variable sqlplus-continue-pattern
  387. which defaults to recognize either a right-justified number padded to four 
  388. characters followed by a space or asterisk, or simply five spaces.  A line
  389. ending with \";\" or \" /\" is also considered the end of a statement.
  390. A new line inserted into a prior statement must be indented at least five
  391. spaces to be included when the statement is re-executed. 
  392.  
  393. The output from a List command is also recognized as a valid SQL*Plus
  394. statement; the 'List' command itself is stripped out (as are 'Get' and 'Run').
  395.  
  396. When a complex SQL statement is executed, it may take a long time before
  397. the output is generated.  Emacs may appear to be hung since no keystrokes
  398. are accepted until the first character of output arrives.  In this situation
  399. '\\[keyboard-quit]' may be used to force emacs to stop waiting for output.
  400. You may then switch to another buffer to perform other work while you wait
  401. or press '\\[interrupt-sqlplus-subjob]' to cancel the current SQL command."
  402.   (interactive)
  403.   (let ((process (get-buffer-process (current-buffer))))
  404.     (if (not process) 
  405.     (error "Current buffer has no process.  Use 'M-x sqlplus' to start SQL*Plus process.")
  406.     )
  407.  
  408.     (cond
  409. ; last line of buffer and only one input line
  410.      ((and (save-excursion (end-of-line) (eobp)) 
  411.        (<= (count-lines (process-mark process) (point)) 1))
  412.     (end-of-line)
  413.         (sqlplus-send-line process)
  414.      )
  415.  
  416. ; within last multi-line command of buffer 
  417.      ((not (save-excursion (re-search-forward sql-prompt (point-max) t)))
  418.     (let ((command-lines (sqlplus-get-command)))
  419.       (sqlplus-kill-command t)        ; clear existing command lines
  420.       (while command-lines            ; send command lines
  421.         (insert (car command-lines))
  422.         (sqlplus-send-line process)
  423.         (setq command-lines (cdr command-lines))
  424.       )
  425.     )
  426.       )
  427. ; otherwise - prior command in buffer
  428.      (t                                 
  429.     (or (save-excursion
  430.           (beginning-of-line)
  431.           (looking-at (concat sql-prompt "\\|" sql-continue-pattern)))
  432.         (error "This is not a valid SQL*plus command."))
  433.     (let ((command-lines (sqlplus-get-command)))
  434.          (goto-char (point-max))
  435.          (sqlplus-kill-command t)     ; clear pending command (if any)
  436.          (while command-lines
  437.         (insert (car command-lines))
  438.         (sqlplus-send-line process)
  439.         (setq command-lines (cdr command-lines))
  440.          )
  441.     )
  442.     )
  443.   ) ;end cond
  444.  ) ;end let
  445.  (setq sqlplus-stack-pointer 0)
  446. )                    ; end defun
  447.  
  448. (defun sqlplus-send-line (process)      ; called from sqlplus-execute-command
  449.   (insert ?\n)
  450.   (let ((command (buffer-substring (process-mark process) (point)))
  451.     (temp-file (expand-file-name "~/sqlplus.buf")))
  452.     (move-marker last-output-start (point))
  453. ; trap EDIT command - must be the only word on the line
  454.     (if (string-match "^ *edit\\s-*\\(\\w*\\)[ ;]*$" command) 
  455.       (let (command-lines 
  456.         (edit-file-name (save-excursion (and 
  457.                          (re-search-backward "edit\\s-+\\([^ \t\n;]+\\)" 
  458.                                  (process-mark process) t)
  459.                          (buffer-substring (match-beginning 1) (match-end 1))
  460.                         )))
  461.  
  462.        )
  463.     (sit-for 0)
  464.     (set-marker (process-mark process) (point))
  465.     (process-send-string process "LIST\n")
  466.     (accept-process-output process)    ; wait for process to respond
  467.     (sleep-for 1)
  468.     (forward-line -1)
  469.     (setq command-lines (sqlplus-get-command)) ; capture command
  470.     (delete-region last-output-start (point))  ; delete listed lines
  471.     (goto-char (point-max))
  472.     (switch-to-buffer-other-window (get-buffer-create (or edit-file-name "*sqlplus-temp*")))
  473.     (if edit-file-name 
  474.         (setq buffer-offer-save t)
  475.     )
  476.     (delete-region (point-min) (point-max))    ; clear buffer
  477.     (while command-lines                   ;insert command lines
  478.       (insert (car command-lines) "\n")
  479.       (setq command-lines (cdr command-lines))
  480.     )
  481.     (insert "/\n")
  482.     (goto-char (point-min))
  483.     (sql-mode)            ;turn on sql-mode
  484.       )
  485. ;   else
  486. ; execute command line
  487.       (process-send-string process command)
  488.       (goto-char (point-max))
  489.       (set-marker (process-mark process) (point))
  490.       (sit-for 0)            ; force display update
  491.       (accept-process-output)        ; wait for process to respond
  492.     )
  493. ; trap QUIT command
  494.     (if (string-match "^ *\\(exit\\|quit\\)[ ;]*$" command)
  495.     (progn
  496.       (if sqlplus-keep-history
  497.           (let ((lines-to-keep (or sqlplus-lines-to-keep 1000)))
  498.         (and (> (count-lines (point-min) (point-max)) lines-to-keep)
  499.              (y-or-n-p 
  500.               (format "Current session is longer than %d lines.  Ok to truncate? " lines-to-keep))
  501.              (sqlplus-drop-old-lines lines-to-keep)
  502.         )
  503.         (sqlplus-save-session "~/.sqlhist")
  504.           )
  505.       )
  506.       (while (get-buffer-process (current-buffer)) 
  507.         (sit-for 1))         ; wait for process to die
  508.       (kill-buffer (current-buffer))
  509.       (and (file-exists-p temp-file) ; if sqlplus.buf exists, delete it
  510.            (delete-file temp-file))
  511.     );end progn
  512.     );end if
  513.   );end let
  514. )
  515.  
  516. (defun sqlplus-kill-command (command-only-flag)
  517.   "Delete the current SQL command or output generated by last SQL command.
  518. When used at the end of the buffer, serves as an undo command.
  519.  
  520. If point is within a valid SQL statement, delete region from SQL> prompt 
  521. before point to end of buffer, otherwise delete all text between the end 
  522. of the prior SQL statement and the end of the buffer."
  523.   (interactive "P")
  524.   (let ((process (get-buffer-process (current-buffer))))
  525.     (if (or command-only-flag
  526.         (save-excursion
  527.           (beginning-of-line)
  528.           (looking-at (concat sql-prompt ".+\\|" sql-continue-pattern))
  529.         )
  530.      )
  531.     ;then - delete command and everything beyond
  532.     (progn
  533.       (delete-region (progn 
  534.                (re-search-backward sql-prompt (point-min) t) 
  535.                (point))
  536.              (point-max))
  537.       (process-send-string process "\n")    ; generate new SQL> prompt
  538.       (goto-char (point-max))
  539.       (set-marker (process-mark process) (point))
  540.       (sit-for 0)                ; update display
  541.       (accept-process-output process)    ; wait for prompt
  542.     )
  543.     ;else - delete output from prior command, leaving cursor at end of command
  544.       (beginning-of-line)
  545.       (or (re-search-backward sql-prompt (point-min) t)
  546.       (error "Nothing to kill"))
  547.       (set-marker (process-mark process) (match-end 0))
  548.       (sqlplus-get-command)        ; convenient way to find end of command
  549.       (forward-char -1)            ; back up one character
  550.       (delete-region (point) (point-max))
  551.     );end if
  552.   )
  553. )
  554.  
  555. (defun sqlplus-get-command ()
  556.   (interactive)
  557.   (let ((line "") command-lines)
  558.     (end-of-line)
  559.     (or (re-search-backward sql-prompt (point-min) t)
  560.     (error "Unable to execute this command"))
  561.     (goto-char (match-end 0))       ; skip past prompt
  562.     (setq command-lines             ; initialize command-lines list
  563.         (if (looking-at "l$\\|list$\\|r$\\|run$\\|get .*\\|edit") ;ignore LIST,RUN,GET,EDIT
  564.         nil
  565.       (list (setq line 
  566.                   (buffer-substring (point) (progn (end-of-line) (point)))))
  567.     )
  568.     )
  569.     (forward-line)
  570.     (while (and                                      ; while previous line 
  571.         (not (string-match "^\\(.*;$\\| */\\)$" line)) ; does not end in / or ;
  572.         (looking-at sql-continue-pattern))       ; and this is a cont. line
  573.          (goto-char (match-end 0))                   ; skip over prompt
  574.      (setq line (buffer-substring (point) (progn (end-of-line) (point))))
  575.      (setq command-lines (append command-lines (list line)))
  576.      (forward-line)
  577.     )
  578.     command-lines          ; return command-lines as value of function
  579. ))
  580.  
  581. (defun interrupt-sqlplus-subjob ()
  582.   "Interrupt this shell's current subjob."
  583.   (interactive)
  584.   (interrupt-process nil t))
  585.  
  586. (defun show-sqlplus-output ()
  587.   "Display most recent batch of output at top of window.
  588. Also put cursor there."
  589.   (interactive)
  590.   (goto-char last-output-start)
  591. )
  592.  
  593. (defun sql-send-buffer (prefix-arg)
  594.   "Execute all SQL*Plus commands defined in current buffer.
  595. Output is displayed in the *sqlplus* buffer."
  596.   (interactive "P")
  597.   (sql-send-region (point-min) (point-max) prefix-arg)
  598. )
  599.  
  600. (defun sql-send-region (start end prefix-arg)
  601.   "Execute all SQL*Plus commands defined between point and mark.
  602. Output is displayed in the *sqlpus* buffer."
  603.   (interactive "r\nP")
  604.   (let (process this-buffer temp-file-name imbedded-variables (temp-buffer nil))
  605.    (setq this-buffer (current-buffer))
  606.    (or (setq process (get-buffer-process "*sqlplus*")) ; look for process
  607.        (setq process (sqlplus-start)) ; or create process
  608.        (error "Unable to create SQL*plus session.")) 
  609. ;    (setq temp-file-name (format "/tmp/sqlbuf.%d" (process-id process)))
  610.     (setq temp-file-name (expand-file-name "~/sqlplus.buf"))
  611.     (set-buffer this-buffer) 
  612.     (if (and (null prefix-arg)        ; if no prefix argument
  613.          (save-excursion        ; look for 'INTO :' or ':variable'
  614.            (goto-char start)
  615.            (re-search-forward "\\binto\\s-+:\\|\\s-:\\w+" end t)))
  616.     (progn
  617.       (setq temp-buffer (get-buffer-create "*sql-temp*"))
  618.       (set-buffer temp-buffer)
  619.       (set-syntax-table sql-mode-syntax-table) ; important for regular expressions
  620.       (erase-buffer)
  621.       (insert-buffer-substring this-buffer start end) ; insert region
  622.       (skip-chars-backward "\n\t ")    ; trim trailing whitespace
  623.       (if (save-excursion
  624.         (forward-char -1)    ; back up one
  625.         (not (looking-at ";\\|/")) ; last character is not ; or /
  626.           )
  627.         (insert "\n/\n")        ; add "/" so statement is executed
  628.       )
  629.       (goto-char (point-min))    ; delete INTO clause
  630.       (if (re-search-forward "\\binto\\s-+:" (point-max) t)
  631.         (delete-region (match-beginning 0) ; delete between INTO & FROM
  632.                    (progn
  633.                  (re-search-forward "\\bfrom\\b" (point-max) t)
  634.                  (match-beginning 0)
  635.                    )
  636.         )
  637.       ) ;endif
  638.       (goto-char (point-min))    ; convert all ":block.field" to "&block_field"
  639.       (replace-regexp ":\\(\\w+\\)\\." "&\\1_" nil)
  640.       (goto-char (point-min))    ; convert all remaining ":" to "&"
  641.       (replace-string ":" "&" nil)
  642.       (goto-char (point-min))
  643.       (while (re-search-forward "&\\w+" (point-max) t)
  644.         (let ( (wbeg (match-beginning 0)) (wend (match-end 0)) )
  645.          (if (> (- wend wbeg) 30)    ; if word > 30 characters
  646.              (delete-region (+ wbeg 1) (- wend 30)) ; truncate to 30
  647.          )
  648.         )
  649.       )
  650.       (setq start (point-min))    ; reset start & end for write-region
  651.       (setq end (point-max))
  652.     ) ;end progn
  653.     ) ;endif 
  654.     (setq imbedded-variables (save-excursion     ; look for &, accept, acc
  655.                    (goto-char start)
  656.                    (re-search-forward "&\\|\\bacc\\(ept\\)?\\b" end t)))
  657.     (write-region start end temp-file-name nil 0) ; write temp file
  658.     (switch-to-buffer-other-window "*sqlplus*")
  659.     (goto-char (point-max))
  660.     (recenter 0)
  661.     (insert (format "\nOutput from buffer '%s':\n" 
  662.             (buffer-name this-buffer)))
  663.     (set-marker (process-mark process) (point))
  664.     (sit-for 0)                ; update display
  665.     (process-send-string process (concat "@" temp-file-name "\n"))
  666.     (if temp-buffer (kill-buffer temp-buffer))
  667.     (if imbedded-variables        ; stay in *sqlplus* buffer to
  668.     (goto-char (point-max))        ; allow entry of variables
  669.       (switch-to-buffer-other-window this-buffer)
  670.     )
  671.   )
  672. )
  673.  
  674. (defun sqlplus-back-command (arg)
  675.   "Move to the SQL*plus command before current position.
  676. With prefix argument, move to ARG'th previous command."
  677.   (interactive "p")
  678.   (if (save-excursion 
  679.     (beginning-of-line)
  680.     (re-search-backward sql-prompt (point-min) t arg)
  681.       )
  682.       (goto-char (match-end 0))
  683.     (error "No previous SQL*plus command.")
  684.   )
  685. )
  686.   
  687. (defun sqlplus-forward-command (arg)
  688.   "Move to the SQL*plus command after current position.
  689. With prefix argument, move to ARG'th previous command."
  690.   (interactive "p")
  691.   (if (re-search-forward sql-prompt (point-max) t arg)
  692.       nil
  693.     (error "No next SQL*plus command.")
  694.   )
  695. )
  696.  
  697. (defun sqlplus-previous-command (arg)
  698.   "Recall the previous SQL*plus command from the command stack.
  699. With prefix argument, recall the command ARG commands before the current
  700. stack pointer."
  701.   (interactive "p")
  702. ; - clear current pending command
  703.   (goto-char (process-mark (get-buffer-process (current-buffer))))
  704.   (delete-region (point) (point-max))
  705.  
  706. ; - increment stack pointer by arg
  707.   (setq sqlplus-stack-pointer (+ sqlplus-stack-pointer arg))
  708.   (if (< sqlplus-stack-pointer 0)
  709.       (progn (setq sqlplus-stack-pointer 0)
  710.          (error "At last command."))
  711.   )
  712.   ;if there is a prior command    
  713.   (if (re-search-backward (concat sql-prompt ".+")  ; skip empty prompts
  714.               (point-min) t sqlplus-stack-pointer)
  715.   ;then
  716.       (let ((command-lines (sqlplus-get-command)) col)
  717.     (goto-char (point-max))
  718.     (setq col (current-column))
  719.     (while command-lines
  720.       (indent-to col)
  721.       (insert (car command-lines))
  722.       (setq command-lines (cdr command-lines))
  723.       (if command-lines (insert ?\n))
  724.     )
  725.         (message (if (> sqlplus-stack-pointer 0)
  726.              (format "#%d" sqlplus-stack-pointer)
  727.            ""))
  728.       )
  729.   ;else
  730.     (setq sqlplus-stack-pointer (- sqlplus-stack-pointer arg)) ; reset
  731.     (error "No previous SQL*plus command.")
  732.   )
  733. )
  734.  
  735. (defun sqlplus-next-command (arg)
  736.   "Recall the next SQL*plus command from the command stack.
  737. With prefix argument, recall the command ARG commands after the current
  738. stack pointer."
  739.   (interactive "p")
  740.   (sqlplus-previous-command (- arg))
  741. )
  742.  
  743. (defun sqlplus-end-of-buffer ()
  744.   "Move cursor to end of buffer."
  745.   (interactive)
  746.   (goto-char (point-max))
  747. )
  748.  
  749. (defun sqlplus-reset-buffer ()
  750.   "Reset SQL*Plus buffer to contain only command history, not output.
  751. Commands of one or fewer characters (/, l, r, etc.) are not retained."
  752.   (interactive)
  753.   (and (y-or-n-p 
  754.     (format "Delete all output lines from buffer %s? " (buffer-name)))
  755.        (let ((line "") (process (get-buffer-process (current-buffer))) start)
  756.      (message "Deleting output lines...")
  757.      (goto-char (point-min))
  758.      (setq start (point))
  759.      (while (re-search-forward (concat sql-prompt "..+") (point-max) t)
  760.        (goto-char (match-end 1))
  761.        (setq line (buffer-substring (point) (progn (end-of-line) (point))))
  762.        (beginning-of-line)
  763.        (delete-region start (point))
  764.        (forward-line)
  765.        (while (and            ; skip past SQL statement
  766.            (not (string-match "^\\(.*;$\\| */\\)$" line))
  767.            (looking-at sql-continue-pattern)) ; and this is a cont. line
  768.          (goto-char (match-end 0))                   ; skip over prompt
  769.          (setq line (buffer-substring (point) (progn (end-of-line) (point))))
  770.          (forward-line)
  771.        )
  772.        (setq start (point))
  773.      )
  774.      (goto-char (point-max))
  775.      (delete-region start (point))
  776.      (process-send-string process "\n")    ; generate new SQL> prompt
  777.      (goto-char (point-max))
  778.      (set-marker (process-mark process) (point))
  779.      (sit-for 0)                ; update display
  780.      (accept-process-output)        ; wait for prompt
  781.      (message "Deleting output lines...Done.")
  782.        )
  783.   )
  784. )
  785.  
  786. (defun sqlplus-drop-old-lines (lines-to-keep)
  787.   "Delete old lines from current buffer.
  788. Number of lines to keep is determined by the variable sqlplus-lines-to-keep.
  789. With prefix argument, keep the last ARG lines."
  790.   (interactive "P")
  791.   (delete-region (save-excursion
  792.            (goto-char (point-min))
  793.            (point))
  794.          (save-excursion
  795.            (goto-char (point-max))
  796.            (forward-line (- (or lines-to-keep 
  797.                     sqlplus-lines-to-keep 
  798.                     1000)))
  799.            (point)))
  800. )
  801.  
  802. (defun sqlplus-save-session (filename)
  803. "Save current SQL*Plus session to FILENAME."
  804.   (interactive "FFile to save session to: ")        
  805.   (save-excursion
  806.     (if (or (null filename) (string= filename ""))
  807.     (setq filename "~/.sqlhist"))
  808.     (message (format "Saving %s..." filename))
  809.     (write-region (progn
  810.             (goto-char (point-min))
  811.             (re-search-forward sql-prompt (point-max) t)
  812.             (match-end 0))
  813.           (progn
  814.             (goto-char (point-max))
  815.             (re-search-backward sql-prompt (point-min) t)
  816.             (match-end 0))
  817.           (expand-file-name filename) nil 0)
  818.     (message (format "Saving %s...done" filename))
  819.   )
  820. )
  821.  
  822. (defun sqlplus-copy-word ()
  823.   "Copy current word to the end of buffer, inserting SELECT keyword 
  824. or commas if appropriate."
  825.   (interactive)
  826.   (let (word preceding-word)
  827.     (save-excursion
  828.       (setq word (buffer-substring    ; extract word
  829.           (progn (forward-char 1) (backward-word 1) (point))
  830.           (progn (forward-word 1) (point))))
  831.       (goto-char (point-max))        ; goto end of buffer
  832.       (cond
  833.        ; sitting at empty command line
  834.        ((save-excursion (beginning-of-line) 
  835.             (looking-at (concat sql-prompt "$")))
  836.     (insert "SELECT ")
  837.        )
  838.        ; on same line as SELECT or ORDER BY, but other words already inserted
  839.        ((save-excursion (re-search-backward " select .+\\| order by .+" 
  840.               (save-excursion (beginning-of-line) (point)) t))
  841.     (insert ", ")
  842.        )
  843.        ; Otherwise
  844.        (t
  845.     (if (eq (preceding-char) ? )    ; if preceding character = space
  846.         nil
  847.       (insert " ")
  848.     )
  849.        )
  850.       );end case
  851.       (insert word)
  852.       (message (format "\"%s\" copied" word))
  853.     )
  854.   )
  855. )
  856.