From: markl@llnl.gov (Mark Levinson)
Newsgroups: gnu.emacs.sources
Subject: using sqlplus and emacs
Date: 12 Nov 92 17:11:24 GMT
Reply-To: markl@llnl.gov
Organization: Lawrence Livermore National Lab


Here are two files:    sql-mode.el and SQLMODE.DOC for using sqlplus with emacs:


---

;; sql-mode.el - Oracle SQL*Plus interface
;;
;; Author:  Jim Lange, Oracle Corporation
;; Date:    27-MAR-90
;; Bugs to: jlange@us.oracle.com
;;
;; $Header: /home/jlange/emacs/RCS/sql-mode.el,v 1.2 90/04/10 07:11:42 jlange Exp Locker: jlange $
;; 
;; Revision History:
;;   19-APR-90 (jlange) - Trap EXIT/QUIT and terminate session.
;;                      - Trap EDIT, but just print message.
;;                      - Allow multiple sqlplus sessions by renaming current
;;                        buffer to new name and executing sqlplus again.
;;                      - Set left-margin to 5 in sqlplus-mode so that
;;                        newline-and-indent (C-j) will indent.
;;                      - Added (accept-process-output process) in 
;;                        sqlplus-kill-command to prevent occasional scrambled
;;                        lines when command is executed.
;;                      - Added sqlplus-reset-buffer.
;;   25-APR-90 (jlange) - Treat GET like LIST and RUN--ignore in 
;;                        sqlplus-get-command.
;;                      - Add sqlplus-drop-old-lines.
;;   04-MAY-90 (jlange) - Add sqlplus-copy-word (C-c C-w).
;;                      - Enhance sqlplus-kill-command to delete command or
;;                        command output, depending on location of point.
;;   11-MAY-90 (jlange) - In sql-send-region, detect imbedded SQL statement 
;;                        format (used in SQL*FORMS, SQL*REPORT, PRO*C, etc.) 
;;                        and convert to standard SQL before executing (remove
;;                        INTO clause and convert :WORD to &WORD).
;;                      - Automatically load and save session history based
;;                        on the variable sqlplus-keep-history.
;;   05-JUN-90 (jlange) - Delete ~/sqlplus.buf when exiting.
;;                      - In sqlplus-send-region, when performing substitutions
;;                        in statements, add "/" at end if not present.
;;   12-JUN-90 (jlange) - In sqlplus-send-region, look for :text.text and convert
;;                        to &text_text (used in SQL*Forms for block.field).
;;   07-SEP-90 (jlange) - Removed process argument from accept-process-output in
;;                        sql-send-line to prevent apparent lockup after issuing
;;                        complex SQL statement.
;;                      - Trap EDIT command and open new emacs buffer containing
;;                        command text.  (new revision 1.2)
;;   08-MAY-91 (jlange) - In sql-send-region, truncate &-variables to 30 characters
;;                        before executing.
;;---------------------------------------------------------------------------------

;; Copyright (C) 1990 Free Software Foundation, Inc., and Jim Lange.
;;
;; This file is part of GNU Emacs.  It is derived from 18.55's shell.el.
;;
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.
;;
;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

;;----------------------------------------------------------------------

;; This file contains function definitions for two new Emacs major modes,
;; sql-mode and sqlplus-mode.  Sql-mode is for editing SQL statements in
;; a standard text buffer.  Sqlplus-mode is used in conjunction with the 
;; sqlplus function which runs SQL*Plus as an inferior process (similar to 
;; shell-mode).
;;
;; Sql-mode is provided primarily as a convenience so that SQL statements 
;; may be sent to SQL*Plus running in another buffer.  Eventually it may
;; also provide automatic formatting of SQL statements based on Oracle 
;; indentation standards (if they exist).
;;
;; Both modes turn on abbrev-mode and share a mode-specific abbreviation table
;; with some predefined abbreviations.  Users may add to these or load in their
;; own.  Abbrev-mode may be turned off in a user defined hook routine.
;;
;; The following commands should be added to a global init file or to any 
;; user's .emacs file to conveniently use the new sql modes.
;;
;;      (autoload 'sqlplus "sql-mode" 
;;        "Run an interactive SQL*plus session in a separate buffer." t)
;;
;;      (autoload 'sql-mode "sql-mode"
;;        "Major mode for editing SQL*plus batch files." t)
;;
;;      (setq auto-mode-alist (cons '("\\.sql$" . sql-mode) auto-mode-alist))
;;
;; Possible Enhancements:
;;   - Detect actual prompt string and set sql-prompt variable appropriately.
;;
;;   - Suggestions?

(provide 'sql-mode)

;; define variables
(defvar sqlplus-startup-message
  (concat 
   "Emacs SQL*Plus Interpreter:  by Jim Lange of Oracle Corporation\n"
   (substring "$Revision: 1.2 $" 1 -1)
   "\n\nCopyright (c) 1990 Free Software Foundation, Inc., and Jim Lange.\n"
   "------------------------------")
  "Message displayed when \\[sqlplus] is executed.")

(defvar last-output-start nil
  "In a sqlplus-mode buffer, marker for beginning of last batch of output.")
(defvar sql-prompt nil
  "In a sqlplus-mode buffer, string containing prompt text.")
(defvar sql-continue-pattern nil
  "In a sqlplus-mode buffer, regular expression for continuation line prompt.")
(defvar sqlplus-username-password nil
  "The username/password to use when starting SQL*Plus.")
(defvar sqlplus-stack-pointer 0
  "Current command recalled from history of commands.")
(defvar sqlplus-keep-history nil
  "If non-nil, save current session in file .sqlhist when exiting.")
(defvar sqlplus-lines-to-keep 1000
  "Number of lines to keep in a SQL*Plus buffer when \\[sqlplus-drop-old-lines] is executed.")
(defvar sqlplus-mode-map nil)
(defvar sql-mode-map nil)
(defvar sql-mode-syntax-table nil
  "Syntax table used while in SQL and SQL*Plus modes.")
(defvar sql-mode-abbrev-table nil
  "Abbrev table used in SQL and SQL*Plus modes.")

;; initialize syntax table
(if sql-mode-syntax-table
    ()
  (setq sql-mode-syntax-table (make-syntax-table))
  (modify-syntax-entry ?/ ". 14" sql-mode-syntax-table)   ; comment start
  (modify-syntax-entry ?* ". 23" sql-mode-syntax-table)
  (modify-syntax-entry ?+ "." sql-mode-syntax-table)
  (modify-syntax-entry ?- "." sql-mode-syntax-table)
  (modify-syntax-entry ?= "." sql-mode-syntax-table)
  (modify-syntax-entry ?% "w" sql-mode-syntax-table)
  (modify-syntax-entry ?< "." sql-mode-syntax-table)
  (modify-syntax-entry ?> "." sql-mode-syntax-table)
  (modify-syntax-entry ?& "w" sql-mode-syntax-table)
  (modify-syntax-entry ?| "." sql-mode-syntax-table)
  (modify-syntax-entry ?_ "w" sql-mode-syntax-table)     ; make _ part of words
  (modify-syntax-entry ?\' "\"" sql-mode-syntax-table))

;; initialize abbreviations
(if sql-mode-abbrev-table
    nil
  (define-abbrev-table 'sql-mode-abbrev-table ())
  (let ((abbrevs-changed nil))
    (define-abbrev sql-mode-abbrev-table  "d"   "describe" nil)
    (define-abbrev sql-mode-abbrev-table  "s"   "select"   nil)
    (define-abbrev sql-mode-abbrev-table  "f"   "from"     nil)
    (define-abbrev sql-mode-abbrev-table  "w"   "where"    nil)
    (define-abbrev sql-mode-abbrev-table  "o"   "order by" nil)
    (define-abbrev sql-mode-abbrev-table  "l"   "like"     nil)
    (define-abbrev sql-mode-abbrev-table  "i"   "in ("     nil)
    (define-abbrev sql-mode-abbrev-table  "g"   "group by" nil)
    (define-abbrev sql-mode-abbrev-table  "h"   "having"   nil)
    (define-abbrev sql-mode-abbrev-table  "n"   "not"      nil)
  )
)

;;-----------------------------

(defun sql-mode ()
  "Major mode for editing SQL*Plus batch files.
\\{sql-mode-map}
sql-send-buffer and sql-send-region are commands that will send SQL*Plus
commands defined in the current buffer to SQL*Plus to be executed.  Output
is displayed in the *sqlplus* buffer (which will open as separate window
if it does not already exist).  (use '\\[describe-mode]' while in *sqlplus*
buffer for information on sqlplus-mode.)

Entry to this mode calls the value of sqlplus-mode-hook with no args,
if that value is non-nil.  Abbrev-mode is also enabled with the following
abbreviations available by default:

        s  ->  Select
        f  ->  From
        w  ->  Where
        o  ->  Order By

Use \\[list-abbrevs] for a full list.

If the SQL statements to be executed contain variables prefixed with colons
or INTO clauses, the colons are converted into ampersands and the INTO clauses
are removed before being sent to SQL*Plus.  This provides compatibility with
Pro*C, SQL*Report, and SQL*Forms (.inp files).  For example,

     SELECT SYSDATE + :days_added INTO :variable FROM SYSTEM.DUAL

is converted to

     SELECT SYSDATE + &days_added FROM SYSTEM.DUAL

and the user is prompted to enter the value of days_added."

  (interactive)
  (setq major-mode 'sql-mode)
  (setq mode-name "SQL")
  (use-local-map sql-mode-map)
  (set-syntax-table sql-mode-syntax-table)
  (setq local-abbrev-table sql-mode-abbrev-table)
  (abbrev-mode 1)
  (setq abbrev-all-caps 1)
  (setq require-final-newline t)
  (run-hooks 'sql-mode-hook)
)

(if sql-mode-map
    nil
  (setq sql-mode-map (make-sparse-keymap))
  (define-key sql-mode-map "\C-c\C-x" 'sql-send-buffer)
  (define-key sql-mode-map "\C-c\C-r" 'sql-send-region)
)

(defun sqlplus-mode ()
  "Major mode for interacting with Oracle SQL*Plus.
Return at end of buffer sends line as input.
Return not at end copies SQL statement to end and executes it.  
\\{sqlplus-mode-map}
This mode is normally invoked by 'M-x sqlplus' (not 'M-x sqlplus-mode').
You will be prompted to enter a username/password combination
to access the Oracle database.  This can be prevented by setting the 
variable sqlplus-username-password in your .emacs file as follows:

     (setq sqlplus-username-password \"myname/mypassword\")

There are two ways of editing and re-executing prior commands.
'\\[sqlplus-back-command]' and '\\[sqlplus-forward-command]' will move to the location 
in the buffer of the previous or next SQL statement, respectively
(based on the command prompt).  The command can then be edited
normally and re-executed by pressing Return.  To insert a newline,
you may press '\\[newline-and-indent]'.  '\\[sqlplus-next-command]' and '\\[sqlplus-previous-command]' 
are similar except the next or previous SQL statement is inserted at
the end of the buffer.  Repeating these commands will clear the
current statement and recall the next or previous statement from the
stack.  For additional information on command execution use 
'\\[describe-key] RTN'.

'\\[show-sqlplus-output]' will move to the beginning of the last ouput
generated by SQL*plus.  This is useful for reviewing the results of 
the last statement.  '\\[sqlplus-end-of-buffer]' moves to the end of the buffer, 
but unlike '\\[end-of-buffer]' (end-of-buffer), it does not set the mark.

'\\[sqlplus-kill-command]' deletes either the current command being entered or
the last output generated by SQL*Plus, depending on when it is used.  
If executed while the cursor is within a SQL statement, the statement and 
any text after it are deleted.  If the cursor is within or at the end of
output generated by SQL*Plus, the output is deleted and the cursor is 
positioned at the end of the SQL statement that generated the ouput.  
'\\[sqlplus-kill-command]' can be used like an undo command to alternately
delete commands and output from the end of the buffer.

The commands sql-send-region and sql-send-buffer can be executed from
another buffer to execute the SQL statements defined by the current
region or entire buffer, respectively.  Output from these commands is
displayed in the *sqlplus* buffer.  The major mode called sql-mode has
these functions bound to key sequences.

Entry to this mode calls the value of sqlplus-mode-hook with no args,
if that value is non-nil.  Abbrev-mode is also enabled with the following
abbreviations available by default:

        s  ->  Select
        f  ->  From
        w  ->  Where
        o  ->  Order By

Use '\\[list-abbrevs]' for a full list.  

If the variable sqlplus-keep-history is non-nil, the current session is
saved in the file ~/.sqlhist and recalled automatically the next time
sqlplus is executed.  The session will only be saved if the EXIT or QUIT
command is used to terminate the SQL*Plus session.  The maximum number of 
lines saved can be set with the variable sqlplus-lines-to-keep which defaults
to 1000.  The command '\\[sqlplus-drop-old-lines]' will truncate the buffer 
to this length at any time.  sqlplus-keep-history and sqlplus-lines-to-keep
should be set in your .emacs file.

If the *sqlplus* buffer is killed with '\\[kill-buffer]', the SQL*Plus
process will automatically be terminated, but the session will not be saved,
even if sqlplus-keep-history is non-nil.

'\\[sqlplus-reset-buffer]' will delete all ouput lines from the buffer, leaving
only commands.  This will significantly shrink the buffer, but retain a full 
history of commands for re-execution."

  (interactive)
  (kill-all-local-variables)
  (setq major-mode 'sqlplus-mode)
  (setq mode-name "SQL*Plus")
  (setq mode-line-process '(": %s"))
  (use-local-map sqlplus-mode-map)
  (set-syntax-table sql-mode-syntax-table)
  (setq local-abbrev-table sql-mode-abbrev-table)
  (make-local-variable 'last-output-start)
  (setq last-output-start (make-marker))
  (make-local-variable 'sql-prompt)
  (setq sql-prompt "^\\(SQL> \\)+")     ;* allows "SQL> SQL> ..."
  (make-local-variable 'sql-continue-pattern)
  (setq sql-continue-pattern "^[ 0-9][ 0-9][0-9][* \t][ \t]\\|     ")
  (setq indent-tabs-mode nil)
  (setq left-margin 5)
  (abbrev-mode 1)
  (setq abbrev-all-caps 1)
  (run-hooks 'sqlplus-mode-hook)
)

(if sqlplus-mode-map
    nil
  (setq sqlplus-mode-map (make-sparse-keymap))
  (define-key sqlplus-mode-map "\C-m" 'sqlplus-execute-command)      
  (define-key sqlplus-mode-map "\t" 'indent-relative)                
  (define-key sqlplus-mode-map "\C-c\C-c" 'interrupt-sqlplus-subjob) 
  (define-key sqlplus-mode-map "\C-c\C-r" 'show-sqlplus-output)      
  (define-key sqlplus-mode-map "\C-c\C-p" 'sqlplus-previous-command) 
  (define-key sqlplus-mode-map "\C-c\C-n" 'sqlplus-next-command)     
  (define-key sqlplus-mode-map "\C-c\C-e" 'sqlplus-end-of-buffer)    
  (define-key sqlplus-mode-map "\C-c\C-b" 'sqlplus-back-command)     
  (define-key sqlplus-mode-map "\C-c\C-f" 'sqlplus-forward-command)  
  (define-key sqlplus-mode-map "\C-c\C-k" 'sqlplus-kill-command)     
  (define-key sqlplus-mode-map "\C-c\C-x" 'sqlplus-reset-buffer)     
  (define-key sqlplus-mode-map "\C-c\C-d" 'sqlplus-drop-old-lines)   
  (define-key sqlplus-mode-map "\C-c\C-w" 'sqlplus-copy-word)        
  (define-key sqlplus-mode-map "\C-c\C-s" 'sqlplus-save-session)     
)

;;-----------------------------

(defun sqlplus ()
  "Start up an interactive SQL*Plus session in a new buffer.
If an active SQL*Plus process already exists, will switch to that
buffer."
  (interactive)
  (let ((process (sqlplus-start)))
    (switch-to-buffer "*sqlplus*")
    (if (and sqlplus-keep-history
	     (file-readable-p (expand-file-name "~/.sqlhist")))
	(progn
	  (sit-for 1)
	  (while (accept-process-output) (sleep-for 1)) 
	  (insert-file-contents (expand-file-name "~/.sqlhist") nil)
	  (goto-char (point-max))
	  (set-marker (process-mark process) (point))
	  (message ".sqlhist loaded")
        )
    );endif
  )
)

(defun sqlplus-start ()
  "Start up an interactive SQL*Plus session in a new buffer."
  (let ((sqlplus-buffer (get-buffer-create "*sqlplus*")) process)
    (setq process			; set process
	  (or				; to the first that is non-nil:
	   (get-buffer-process sqlplus-buffer)    ; current process
	   (progn			          ; or new process
	     (set-buffer sqlplus-buffer)
	     (insert sqlplus-startup-message)
	     (start-process "SQL*plus" sqlplus-buffer "sqlplus" 
		(or sqlplus-username-password
		    (setq sqlplus-username-password 
			  (read-string "Enter SQL*plus username/password: "))))
	   ))
    )
    (set-buffer sqlplus-buffer)
    (goto-char (point-max))
    (set-marker (process-mark process) (point))
    (sqlplus-mode)
    process             ; return process
  )
)

(defun sqlplus-execute-command ()
"When executed at end of buffer, sends text entered since last 
output from SQL*Plus.  When executed while positioned within another
valid command in the buffer, copies command to end of buffer and 
re-executes it.  If point is within a multi-line statement at the end
of the buffer (such as after '\\[sqlplus-previous-command]'), the entire
statement will be cleared and re-entered one line at a time.

Multi-line statements are recognized by the continuation prompt displayed
by SQL*Plus.  This is controlled by the variable sqlplus-continue-pattern
which defaults to recognize either a right-justified number padded to four 
characters followed by a space or asterisk, or simply five spaces.  A line
ending with \";\" or \" /\" is also considered the end of a statement.
A new line inserted into a prior statement must be indented at least five
spaces to be included when the statement is re-executed. 

The output from a List command is also recognized as a valid SQL*Plus
statement; the 'List' command itself is stripped out (as are 'Get' and 'Run').

When a complex SQL statement is executed, it may take a long time before
the output is generated.  Emacs may appear to be hung since no keystrokes
are accepted until the first character of output arrives.  In this situation
'\\[keyboard-quit]' may be used to force emacs to stop waiting for output.
You may then switch to another buffer to perform other work while you wait
or press '\\[interrupt-sqlplus-subjob]' to cancel the current SQL command."
  (interactive)
  (let ((process (get-buffer-process (current-buffer))))
    (if (not process) 
	(error "Current buffer has no process.  Use 'M-x sqlplus' to start SQL*Plus process.")
    )

    (cond
; last line of buffer and only one input line
     ((and (save-excursion (end-of-line) (eobp)) 
	   (<= (count-lines (process-mark process) (point)) 1))
	(end-of-line)
        (sqlplus-send-line process)
     )

; within last multi-line command of buffer 
     ((not (save-excursion (re-search-forward sql-prompt (point-max) t)))
	(let ((command-lines (sqlplus-get-command)))
	  (sqlplus-kill-command t)        ; clear existing command lines
	  (while command-lines            ; send command lines
	    (insert (car command-lines))
	    (sqlplus-send-line process)
	    (setq command-lines (cdr command-lines))
	  )
	)
      )
; otherwise - prior command in buffer
     (t                                 
	(or (save-excursion
	      (beginning-of-line)
	      (looking-at (concat sql-prompt "\\|" sql-continue-pattern)))
	    (error "This is not a valid SQL*plus command."))
	(let ((command-lines (sqlplus-get-command)))
	     (goto-char (point-max))
	     (sqlplus-kill-command t)     ; clear pending command (if any)
	     (while command-lines
		(insert (car command-lines))
		(sqlplus-send-line process)
		(setq command-lines (cdr command-lines))
	     )
	)
    )
  ) ;end cond
 ) ;end let
 (setq sqlplus-stack-pointer 0)
)					; end defun

(defun sqlplus-send-line (process)      ; called from sqlplus-execute-command
  (insert ?\n)
  (let ((command (buffer-substring (process-mark process) (point)))
	(temp-file (expand-file-name "~/sqlplus.buf")))
    (move-marker last-output-start (point))
; trap EDIT command - must be the only word on the line
    (if (string-match "^ *edit\\s-*\\(\\w*\\)[ ;]*$" command) 
      (let (command-lines 
	    (edit-file-name (save-excursion (and 
					     (re-search-backward "edit\\s-+\\([^ \t\n;]+\\)" 
								 (process-mark process) t)
					     (buffer-substring (match-beginning 1) (match-end 1))
					    )))

	   )
	(sit-for 0)
	(set-marker (process-mark process) (point))
	(process-send-string process "LIST\n")
	(accept-process-output process)	; wait for process to respond
	(sleep-for 1)
	(forward-line -1)
	(setq command-lines (sqlplus-get-command)) ; capture command
	(delete-region last-output-start (point))  ; delete listed lines
	(goto-char (point-max))
	(switch-to-buffer-other-window (get-buffer-create (or edit-file-name "*sqlplus-temp*")))
	(if edit-file-name 
	    (setq buffer-offer-save t)
	)
	(delete-region (point-min) (point-max))    ; clear buffer
	(while command-lines		           ;insert command lines
	  (insert (car command-lines) "\n")
	  (setq command-lines (cdr command-lines))
	)
	(insert "/\n")
	(goto-char (point-min))
	(sql-mode)			;turn on sql-mode
      )
;   else
; execute command line
      (process-send-string process command)
      (goto-char (point-max))
      (set-marker (process-mark process) (point))
      (sit-for 0)			; force display update
      (accept-process-output)		; wait for process to respond
    )
; trap QUIT command
    (if (string-match "^ *\\(exit\\|quit\\)[ ;]*$" command)
	(progn
	  (if sqlplus-keep-history
	      (let ((lines-to-keep (or sqlplus-lines-to-keep 1000)))
		(and (> (count-lines (point-min) (point-max)) lines-to-keep)
		     (y-or-n-p 
		      (format "Current session is longer than %d lines.  Ok to truncate? " lines-to-keep))
		     (sqlplus-drop-old-lines lines-to-keep)
		)
		(sqlplus-save-session "~/.sqlhist")
	      )
	  )
	  (while (get-buffer-process (current-buffer)) 
	    (sit-for 1))		 ; wait for process to die
	  (kill-buffer (current-buffer))
	  (and (file-exists-p temp-file) ; if sqlplus.buf exists, delete it
	       (delete-file temp-file))
	);end progn
    );end if
  );end let
)

(defun sqlplus-kill-command (command-only-flag)
  "Delete the current SQL command or output generated by last SQL command.
When used at the end of the buffer, serves as an undo command.

If point is within a valid SQL statement, delete region from SQL> prompt 
before point to end of buffer, otherwise delete all text between the end 
of the prior SQL statement and the end of the buffer."
  (interactive "P")
  (let ((process (get-buffer-process (current-buffer))))
    (if (or command-only-flag
	    (save-excursion
	      (beginning-of-line)
	      (looking-at (concat sql-prompt ".+\\|" sql-continue-pattern))
	    )
	 )
    ;then - delete command and everything beyond
	(progn
	  (delete-region (progn 
			   (re-search-backward sql-prompt (point-min) t) 
			   (point))
			 (point-max))
	  (process-send-string process "\n")	; generate new SQL> prompt
	  (goto-char (point-max))
	  (set-marker (process-mark process) (point))
	  (sit-for 0)				; update display
	  (accept-process-output process)	; wait for prompt
	)
    ;else - delete output from prior command, leaving cursor at end of command
      (beginning-of-line)
      (or (re-search-backward sql-prompt (point-min) t)
	  (error "Nothing to kill"))
      (set-marker (process-mark process) (match-end 0))
      (sqlplus-get-command)		; convenient way to find end of command
      (forward-char -1)			; back up one character
      (delete-region (point) (point-max))
    );end if
  )
)

(defun sqlplus-get-command ()
  (interactive)
  (let ((line "") command-lines)
    (end-of-line)
    (or (re-search-backward sql-prompt (point-min) t)
	(error "Unable to execute this command"))
    (goto-char (match-end 0))       ; skip past prompt
    (setq command-lines             ; initialize command-lines list
        (if (looking-at "l$\\|list$\\|r$\\|run$\\|get .*\\|edit") ;ignore LIST,RUN,GET,EDIT
	    nil
	  (list (setq line 
  	            (buffer-substring (point) (progn (end-of-line) (point)))))
	)
    )
    (forward-line)
    (while (and                                      ; while previous line 
	    (not (string-match "^\\(.*;$\\| */\\)$" line)) ; does not end in / or ;
	    (looking-at sql-continue-pattern))       ; and this is a cont. line
         (goto-char (match-end 0))                   ; skip over prompt
	 (setq line (buffer-substring (point) (progn (end-of-line) (point))))
	 (setq command-lines (append command-lines (list line)))
	 (forward-line)
    )
    command-lines          ; return command-lines as value of function
))

(defun interrupt-sqlplus-subjob ()
  "Interrupt this shell's current subjob."
  (interactive)
  (interrupt-process nil t))

(defun show-sqlplus-output ()
  "Display most recent batch of output at top of window.
Also put cursor there."
  (interactive)
  (goto-char last-output-start)
)

(defun sql-send-buffer (prefix-arg)
  "Execute all SQL*Plus commands defined in current buffer.
Output is displayed in the *sqlplus* buffer."
  (interactive "P")
  (sql-send-region (point-min) (point-max) prefix-arg)
)

(defun sql-send-region (start end prefix-arg)
  "Execute all SQL*Plus commands defined between point and mark.
Output is displayed in the *sqlpus* buffer."
  (interactive "r\nP")
  (let (process this-buffer temp-file-name imbedded-variables (temp-buffer nil))
   (setq this-buffer (current-buffer))
   (or (setq process (get-buffer-process "*sqlplus*")) ; look for process
       (setq process (sqlplus-start)) ; or create process
       (error "Unable to create SQL*plus session.")) 
;    (setq temp-file-name (format "/tmp/sqlbuf.%d" (process-id process)))
    (setq temp-file-name (expand-file-name "~/sqlplus.buf"))
    (set-buffer this-buffer) 
    (if (and (null prefix-arg)		; if no prefix argument
	     (save-excursion		; look for 'INTO :' or ':variable'
	       (goto-char start)
	       (re-search-forward "\\binto\\s-+:\\|\\s-:\\w+" end t)))
	(progn
	  (setq temp-buffer (get-buffer-create "*sql-temp*"))
	  (set-buffer temp-buffer)
	  (set-syntax-table sql-mode-syntax-table) ; important for regular expressions
	  (erase-buffer)
	  (insert-buffer-substring this-buffer start end) ; insert region
	  (skip-chars-backward "\n\t ")	; trim trailing whitespace
	  (if (save-excursion
		(forward-char -1)	; back up one
		(not (looking-at ";\\|/")) ; last character is not ; or /
	      )
	    (insert "\n/\n")		; add "/" so statement is executed
	  )
	  (goto-char (point-min))	; delete INTO clause
	  (if (re-search-forward "\\binto\\s-+:" (point-max) t)
		(delete-region (match-beginning 0) ; delete between INTO & FROM
			       (progn
				 (re-search-forward "\\bfrom\\b" (point-max) t)
				 (match-beginning 0)
			       )
		)
	  ) ;endif
	  (goto-char (point-min))	; convert all ":block.field" to "&block_field"
	  (replace-regexp ":\\(\\w+\\)\\." "&\\1_" nil)
	  (goto-char (point-min))	; convert all remaining ":" to "&"
	  (replace-string ":" "&" nil)
	  (goto-char (point-min))
	  (while (re-search-forward "&\\w+" (point-max) t)
	    (let ( (wbeg (match-beginning 0)) (wend (match-end 0)) )
		 (if (> (- wend wbeg) 30)	; if word > 30 characters
		     (delete-region (+ wbeg 1) (- wend 30)) ; truncate to 30
		 )
	    )
	  )
	  (setq start (point-min))	; reset start & end for write-region
	  (setq end (point-max))
	) ;end progn
    ) ;endif 
    (setq imbedded-variables (save-excursion     ; look for &, accept, acc
			       (goto-char start)
			       (re-search-forward "&\\|\\bacc\\(ept\\)?\\b" end t)))
    (write-region start end temp-file-name nil 0) ; write temp file
    (switch-to-buffer-other-window "*sqlplus*")
    (goto-char (point-max))
    (recenter 0)
    (insert (format "\nOutput from buffer '%s':\n" 
		    (buffer-name this-buffer)))
    (set-marker (process-mark process) (point))
    (sit-for 0)				; update display
    (process-send-string process (concat "@" temp-file-name "\n"))
    (if temp-buffer (kill-buffer temp-buffer))
    (if imbedded-variables		; stay in *sqlplus* buffer to
	(goto-char (point-max))		; allow entry of variables
      (switch-to-buffer-other-window this-buffer)
    )
  )
)

(defun sqlplus-back-command (arg)
  "Move to the SQL*plus command before current position.
With prefix argument, move to ARG'th previous command."
  (interactive "p")
  (if (save-excursion 
	(beginning-of-line)
	(re-search-backward sql-prompt (point-min) t arg)
      )
      (goto-char (match-end 0))
    (error "No previous SQL*plus command.")
  )
)
  
(defun sqlplus-forward-command (arg)
  "Move to the SQL*plus command after current position.
With prefix argument, move to ARG'th previous command."
  (interactive "p")
  (if (re-search-forward sql-prompt (point-max) t arg)
      nil
    (error "No next SQL*plus command.")
  )
)

(defun sqlplus-previous-command (arg)
  "Recall the previous SQL*plus command from the command stack.
With prefix argument, recall the command ARG commands before the current
stack pointer."
  (interactive "p")
; - clear current pending command
  (goto-char (process-mark (get-buffer-process (current-buffer))))
  (delete-region (point) (point-max))

; - increment stack pointer by arg
  (setq sqlplus-stack-pointer (+ sqlplus-stack-pointer arg))
  (if (< sqlplus-stack-pointer 0)
      (progn (setq sqlplus-stack-pointer 0)
	     (error "At last command."))
  )
  ;if there is a prior command    
  (if (re-search-backward (concat sql-prompt ".+")  ; skip empty prompts
			  (point-min) t sqlplus-stack-pointer)
  ;then
      (let ((command-lines (sqlplus-get-command)) col)
	(goto-char (point-max))
	(setq col (current-column))
	(while command-lines
	  (indent-to col)
	  (insert (car command-lines))
	  (setq command-lines (cdr command-lines))
	  (if command-lines (insert ?\n))
	)
        (message (if (> sqlplus-stack-pointer 0)
		     (format "#%d" sqlplus-stack-pointer)
		   ""))
      )
  ;else
    (setq sqlplus-stack-pointer (- sqlplus-stack-pointer arg)) ; reset
    (error "No previous SQL*plus command.")
  )
)

(defun sqlplus-next-command (arg)
  "Recall the next SQL*plus command from the command stack.
With prefix argument, recall the command ARG commands after the current
stack pointer."
  (interactive "p")
  (sqlplus-previous-command (- arg))
)

(defun sqlplus-end-of-buffer ()
  "Move cursor to end of buffer."
  (interactive)
  (goto-char (point-max))
)

(defun sqlplus-reset-buffer ()
  "Reset SQL*Plus buffer to contain only command history, not output.
Commands of one or fewer characters (/, l, r, etc.) are not retained."
  (interactive)
  (and (y-or-n-p 
	(format "Delete all output lines from buffer %s? " (buffer-name)))
       (let ((line "") (process (get-buffer-process (current-buffer))) start)
	 (message "Deleting output lines...")
	 (goto-char (point-min))
	 (setq start (point))
	 (while (re-search-forward (concat sql-prompt "..+") (point-max) t)
	   (goto-char (match-end 1))
	   (setq line (buffer-substring (point) (progn (end-of-line) (point))))
	   (beginning-of-line)
	   (delete-region start (point))
	   (forward-line)
	   (while (and			; skip past SQL statement
		   (not (string-match "^\\(.*;$\\| */\\)$" line))
		   (looking-at sql-continue-pattern)) ; and this is a cont. line
	     (goto-char (match-end 0))                   ; skip over prompt
	     (setq line (buffer-substring (point) (progn (end-of-line) (point))))
	     (forward-line)
	   )
	   (setq start (point))
	 )
	 (goto-char (point-max))
	 (delete-region start (point))
	 (process-send-string process "\n")	; generate new SQL> prompt
	 (goto-char (point-max))
	 (set-marker (process-mark process) (point))
	 (sit-for 0)				; update display
	 (accept-process-output)		; wait for prompt
	 (message "Deleting output lines...Done.")
       )
  )
)

(defun sqlplus-drop-old-lines (lines-to-keep)
  "Delete old lines from current buffer.
Number of lines to keep is determined by the variable sqlplus-lines-to-keep.
With prefix argument, keep the last ARG lines."
  (interactive "P")
  (delete-region (save-excursion
		   (goto-char (point-min))
		   (point))
		 (save-excursion
		   (goto-char (point-max))
		   (forward-line (- (or lines-to-keep 
					sqlplus-lines-to-keep 
					1000)))
		   (point)))
)

(defun sqlplus-save-session (filename)
"Save current SQL*Plus session to FILENAME."
  (interactive "FFile to save session to: ")	    
  (save-excursion
    (if (or (null filename) (string= filename ""))
	(setq filename "~/.sqlhist"))
    (message (format "Saving %s..." filename))
    (write-region (progn
		    (goto-char (point-min))
		    (re-search-forward sql-prompt (point-max) t)
		    (match-end 0))
		  (progn
		    (goto-char (point-max))
		    (re-search-backward sql-prompt (point-min) t)
		    (match-end 0))
		  (expand-file-name filename) nil 0)
    (message (format "Saving %s...done" filename))
  )
)

(defun sqlplus-copy-word ()
  "Copy current word to the end of buffer, inserting SELECT keyword 
or commas if appropriate."
  (interactive)
  (let (word preceding-word)
    (save-excursion
      (setq word (buffer-substring	; extract word
		  (progn (forward-char 1) (backward-word 1) (point))
		  (progn (forward-word 1) (point))))
      (goto-char (point-max))		; goto end of buffer
      (cond
       ; sitting at empty command line
       ((save-excursion (beginning-of-line) 
			(looking-at (concat sql-prompt "$")))
	(insert "SELECT ")
       )
       ; on same line as SELECT or ORDER BY, but other words already inserted
       ((save-excursion (re-search-backward " select .+\\| order by .+" 
			  (save-excursion (beginning-of-line) (point)) t))
	(insert ", ")
       )
       ; Otherwise
       (t
	(if (eq (preceding-char) ? )	; if preceding character = space
	    nil
	  (insert " ")
	)
       )
      );end case
      (insert word)
      (message (format "\"%s\" copied" word))
    )
  )
)

;end if file -------- cut here ---------------------------------------

;This is a seperate file of documentation

Although SQL*Plus is a powerful tool for producing ad hoc queries and
interacting with an Oracle database, the user interface is less than
state-of-the-art.

I am now pleased to announce a new utility available to users of Emacs and
SQL*Plus that allows SQL*Plus to run in an Emacs buffer.  Even if you don't
know Emacs, this utility makes SQL*Plus so much friendlier that you may want
to learn the basics of Emacs just to take advantage of the improvements.  For
those of you who have been using the current version, this version includes
many significant enhancements (see below).

If anyone receives this message that does not yet have access to this utility
but would like to use it, send me a mail message and I can send you the
appropriate files with instructions.

If you are not familiar with Emacs, some of the terms used below may be
foreign to you (buffers, modes, key sequences, etc.).  However, Emacs comes
with an excellent tutorial that will teach you the basics (just press
Control-H, then the letter "T", 'C-h t', after starting Emacs) as well as full
on-line documentation (C-h i).


Description
===========

Here is a brief description of the enhancements that this utility adds to 
SQL*Plus (features added since version 1.1 are indicated with NEW!):

        * Fully compatible with SQL*Plus.  If none of the new features
          are accessed, SQL*Plus looks and behaves exactly like it
          does normally.  You can enter commands one line at a time and
          use all the awkward line editing commands you've become
          accustomed to. 

        * Full history buffer.  All text entered and received in the 
          current session is stored so you can scroll forward and back
          to examine prior results.

NEW!    * Save history between sessions.  You have the option of saving
          your sessions automatically so that the next time SQL*Plus
          is invoked, you are returned to where you left off--with all
          prior input and output intact!

        * Full command line editing.  All of Emacs' editing commands
          may be used to correct errors in the current input line before
          pressing RETURN.  When entering multi-line statements you may
          easily move up to prior lines to change them before executing
          the entire statement.

        * Recall, edit, and re-execute prior commands.  Any prior command
          entered in the buffer may be modified and re-executed.  Also, 
          full cut and paste of commands or parts or commands is supported
          using standard Emacs features.

        * Entry shortcuts.  Single letter abbreviations are provided for
          commonly used keywords.  For example, typing S<space> inserts 
          "SELECT ".  Also, any other keyword, table, or column name
          used earlier may be abbreviated with the first few letters
          (see dynamic expansion below).

NEW!    * Powerful "word grab" feature.  Position the cursor on a column
          or table name and with the press of a key, the text is appended
          to the end of the current SQL statement.  Commas are even inserted
          automatically when appropriate!

NEW!    * Run multiple SQL*Plus sessions simultaneously.  Using Emacs'
          multiple buffer/window features, two or more sessions may be
          active concurrently--each attached to a different database if
          desired.

        * Edit SQL*Plus batch files and execute them with a keystroke.
          You no longer need to use the EDIT or HOST commands to move
          in and out of a full screen editor while refining SQL scripts.  
          Just edit in one Emacs window and with the press of a key, the 
          commands will be executed with the output appearing in a second 
NEW!      window. You can even execute SQL statements imbedded in a 
          SQL*Report, SQL*Forms (.inp), or Pro*C (Fortran, COBOL, etc.) file!

        * On-line help.  Help is available in any mode by pressing C-h m.
          (This is true of all Emacs modes, not just the new SQL modes.) 
          You can also get help on any key sequence with C-h k <key sequence>.

        * Multi-tasking!  Since you are in Emacs, you can perform
          other tasks while you wait for a complex SQL statement to finish
          executing.  Switch to another document, open a new window, 
          compose a letter, run an operating system shell, and if Emacs
          is run from the Unix c-shell, it can be "suspended" and you 
          can return to it later.

This utility is implemented as an Emacs "major mode" for editing text. There
are actually two new major modes.  The first is sqlplus-mode which runs a
SQL*Plus session in a buffer called *sqlplus* and allows you to edit and
re-execute prior commands, as well as providing a history of all interaction.
This mode is similar to shell-mode (and is, in fact, based upon it), but with
additional features specific to SQL*Plus.

Another mode called sql-mode can be used to edit SQL*Plus batch programs which
can then be executed with a keypress with the output appearing in another
window.

Like most other special Emacs modes, the commands unique to these new modes
are accessed using a keystroke sequence of Control-C followed by another
control character.


SQL*Plus Mode
=============

To experiment with the interactive SQL*Plus mode, invoke Emacs with these
options: 

        emacs -l sql-mode -f sqlplus

You will be prompted to enter a username/password combination to access an
Oracle database.  Interaction in the *sqlplus* buffer is exactly like normal
SQL*Plus, including its limited editing capabilities, if all commands are
entered at the end of the buffer.  Prior commands may be recalled, edited, and
re-executed in the following ways.

        1) Manually cursor back up to the command to be edited, make changes,
           then press [Return] when the cursor is on any line of a multi-line
           statement.  This is convenient after a command that produces a
           small amount of output or after a LIST command.

        2) Use C-c C-b and C-c C-f (back/forward command) to move the cursor to
           the previous or next command in the buffer.  Then edit and
           re-execute by pressing [Return].  C-c C-b is also useful after a
           command has produced several pages of output and you wish to begin
           reviewing it from the beginning.  I have these commands bound to
           ESC up-arrow and ESC down-arrow in my .emacs file.

        3) Use C-c C-p and C-c C-n (previous/next command) to recall the prior
           commands directly after the last prompt at the end of the buffer.
           When the desired command has been recalled, press [Return] to
           execute, or edit and then re-execute with [Return].


Typing Shortcuts
----------------

A powerful feature which is built into Emacs is the use of abbreviations.  In
sqlplus-mode, standard abbreviations are automatically available for common
keywords such as DESCRIBE, SELECT, FROM, WHERE, ORDER BY, etc. by entering the
first letter followed by space or tab.  You may add to or modify this table
and save it for later use (consult an Emacs manual or the on-line
documentation for more information on using abreviations).

Dynamic abbreviations are also very useful.  Entering the first few characters
of a table or column name (or any other word) included anywhere in the buffer
and typing M-/ (note: M = ESC) will complete the name; successive M-/'s will
cycle through all matches.  I use this after "DESCRIBE table" so that all
column names are conveniently available.

Another way to quickly and easily build queries after using DESCRIBE is to
position the cursor over (or after) the first column name you wish to SELECT
and press C-c C-w (copy word).  If the command line at the end of the buffer
is blank, "SELECT" will be inserted, followed by the column name.  Repeat this
for each column to be selected and the names will be appended to the line with
commas separating each entry.  This technique may also be used to copy words
from an earlier line of the same statement while entering expressions,
comparisons, or ORDER BY clauses.  Building SELECT statements has never been
so easy!


Saving Your Session
-------------------

Since all output is saved in the buffer, you can easily scroll back to review
prior queries, without having to re-execute them.  You may also save your
session with C-c C-s (or M-x sqlplus-save-session) to any file and re-load it
later with C-x i (insert-file).  A better option is to have Emacs do this
for you when you exit.  By setting the the Emacs variable sqlplus-keep-history
to a non-nil value in your .emacs file, your session will saved in the file
.sqlhist in your home directory and automatically loaded the next time sqlplus
is run (an example of how to set this variable is included later).

Since your .sqlhist file is likely to grow very large after only a few
sessions, I have provided some ways of preventing the history buffer from
growing too large.  When you exit SQL*Plus by typing EXIT or QUIT, the number
of lines in the buffer is compared with the variable sqlplus-lines-to-keep,
which defaults to 1000.  If it is larger, you will be asked if you wish to
tuncate the buffer to the indicated size before saving (the oldest lines will
be deleted).  You can also perform this truncation at any time during your
session with the command C-c C-d (delete old lines).  If you wish to exit
without saving the session, use C-x k (kill-buffer).

Another way to drastically shrink the buffer is with C-c C-x which deletes all
output lines in the buffer, keeping only the commands that have been entered.
This way you still have access to prior commands for re-execution.  

After entering a SQL command that generates an error, or produces a report
that still is not quite correct, rather than recall the command to edit and
re-execute it, use C-c C-k (kill) which deletes either the last output
generated by SQL*Plus or the current command being entered, depending on where
the cursor is when it is used.  If executed while the cursor is within a SQL
statement, the statement and any text after it are deleted.  If the cursor is
within or at the end of output generated by SQL*Plus, the output is deleted
and the cursor is positioned at the end of the SQL statement that generated
the output.  Thus, it can be used like an undo command to alternately delete
commands and output from the end of the buffer.  This helps prevent the buffer
from growing as a result of several "false starts" and makes finding prior
valid commands easier (because you don't have to wade through several bad
commands and their associated output).



which will also kill the process (but will not save your session).


Summary of Commands
-------------------

        C-c C-p   Recall previous command.
        C-c C-n   Recall next command.
        C-c C-b   Back one command.
        C-c C-f   Forward one command.
        C-c C-e   Move end of buffer (like M->, but does not set the mark.)
        C-c C-r   Return to beginning of last output produced by SQL*Plus.
        C-c C-c   Send ^C to interrupt current command execution.
NEW!    C-c C-k   Kill current SQL statement or last output.
NEW!    C-c C-x   Delete all output lines.
NEW!    C-c C-d   Drop old lines (truncate to value of sqlplus-lines-to-keep).
NEW!    C-c C-w   Copy word to end.
NEW!    C-c C-s   Save session to file.

You can generate a list like this while in sqlplus-mode by typing C-h m (Help
with current Mode) or C-h b (Help keyBindings).  You can also get help on any
specific command with C-h k (Help Key) followed by the key sequence.


SQL Edit Mode
=============

In a text buffer, the command 'M-x sql-mode' will enable SQL text editing
mode.  The keyword abbreviations described above are also available in this
mode.  Two special commands are provided to execute the SQL*Plus statements
contained in a sql-mode buffer.  C-c C-x will send the entire buffer to
SQL*Plus.  The output is displayed in the *sqlplus* buffer in a second window
(if SQL*Plus is not currently running, a new buffer is created and the process
started).  C-c C-r will send the commands in the currently defined region
(between point and mark).  This feature may be useful to Pro*C programmers who
need to test SQL statements imbedded in their code.

If the SQL statements to be executed contain variables prefixed with colons
or INTO clauses, the colons are converted into ampersands and the INTO clauses
are removed before being sent to SQL*Plus.  This provides compatibility with
Pro*C, SQL*Report, and SQL*Forms (.inp files).  For example,

     SELECT SYSDATE + :days_added INTO :variable FROM SYSTEM.DUAL

is converted to

     SELECT SYSDATE + &days_added FROM SYSTEM.DUAL

and the user is prompted to enter the value of days_added.  This substitution
process can be prevented by using a prefix argument with the
sqlplus-send-region command.  In practice, this is done by typing 'C-u C-c
C-r'.  Also, variables of the form :block_name.field_name are converted to
&block_name_field_name for convenience when editing .inp files.

In the future, this mode may automatically indent your SQL*Plus code based on
Oracle standards (if they have been defined).  Your suggestions are welcome.


Accessing the New Modes
=======================

If Emacs is started normally, the new modes will not be available until the
new library is loaded with the command:

         M-x load-library RET sql-mode RET      (RET = the RETURN key)

Or Emacs can be invoked with the option '-l sql-mode'.

Once the library is loaded, the commands 'M-x sqlplus' and 'M-x sql-mode' can
be used.  This can be automated by including the following commands in your
.emacs file:

        (autoload 'sql-mode "sql-mode" "SQL editing mode" t)
        (autoload 'sqlplus "sql-mode" "Run SQL*Plus interactively" t)

This will load the sql-mode library the first time either of these commands is
used.

Sql-mode can be invoked automatically whenever a file ending in .sql is edited
by including this command in .emacs:

   (setq auto-mode-alist (cons '("\\.sql$" . sql-mode) auto-mode-alist))

Note: The above statements may eventually be added to the global
initialization files used on each machine where Emacs is loaded
(i.e. site-init.el).


Customizing Your Environment
============================

When sqlplus is initially executed, you will be prompted to enter a username
and password to access an Oracle database.  If you always use the same
username/password combination, this can be suppressed by setting a special
variable in your .emacs file as follows:

        (setq sqlplus-username-password "myname/mypassword")

You may also wish to assign some of the special functions to more convenient
keys on your keyboard.  Each of these modes calls a "hook" routine (like all
other Emacs modes).  By assigning custom routines to these hooks, you can add
additional functionality.  Below are some statements that will assign common
commands used in sqlplus and sql-mode to VT320 function keys.

(global-unset-key "\e\e"              ; allow ESC as a prefix to function keys
(setq sql-mode-hook 'set-sql-keys)
(setq sqlplus-mode-hook 'set-sqlplus-keys)

(defun set-sql-keys ()
  "Customize sql-mode keys."
  (local-set-key "\eOM" 'sql-send-buffer)                 ; Enter
  (local-set-key "\e\eOM" 'sql-send-region))              ; ESC Enter

(defun set-sqlplus-keys ()
  "Customize sqlplus-mode keys."
  (local-set-key "\e\eOA" 'sqlplus-back-command)          ; ESC up-arrow
  (local-set-key "\e\eOB" 'sqlplus-forward-command)       ; ESC down-arrow
  (local-set-key "\e\eOD" 'sqlplus-previous-command)      ; ESC left-arrow
  (local-set-key "\e\eOC" 'sqlplus-next-command)          ; ESC right-arrow
  (local-set-key "\e\r" 'sqlplus-end-of-buffer))          ; ESC Return

These key bindings will only be active in the buffers running the new modes
and thus will not affect other functions assigned to the same keys in other
buffers.

SQL*Plus reads and executes the contents of the file login.sql in your home
directory when it starts up.  If you have modified the default parameters you
may need to make changes to this file for compatibility with sqlplus-mode.

The current version requires that the SQL*Plus prompt be "SQL> " and that
continuation prompts are numbered lines (the default), so do not change the
settings of the SQL*Plus variables 'sqlprompt' or 'sqlnumber'.  Page pause
should be left OFF (set pause off) since you can page forward and back using
Emacs commands (although setting pause ON will still work).

Since Emacs automatically wraps long lines you can set the line length used by
SQL*Plus to the maximum value (set linesize 500).  Then if your terminal
supports wide displays or variable size windows, long lines will always wrap
to fit (however, this will also affect the centering of titles).


Known Bugs 
==========

When entering a new command, if you move up to a prior line (of the same
multi-line statement), make a change, then move back to the last line and
finish entering it, SQL*Plus will not see the change--it will only process the
last line entered.  However, if you move up to any line except the last one
before pressing [Return], all lines will be re-evaluated when the command is
executed.

If you discover additional bugs or think of some good enhancements, send them
via email to jlange@us.oracle.com, or call me at (415) 506-4669.

    -Jim Lange




Newsgroups: gnu.emacs.sources