changeset 290:67ff100baeaa

merged abbrev changes
author catherine@dellzilla
date Mon, 23 Mar 2009 10:16:20 -0400
parents e7578e7ff9dd (diff) 3cade02da892 (current diff)
children 2103616cffcd
files sqlpython/sqlpyPlus.py
diffstat 6 files changed, 1097 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/refresh.bash	Mon Mar 23 10:16:20 2009 -0400
@@ -0,0 +1,2 @@
+sphinx-build -b html source build
+scp -r build catherine@$tummy:/var/www/sqlpython
\ No newline at end of file
--- a/docs/source/capabilities.rst	Mon Mar 23 08:36:02 2009 -0400
+++ b/docs/source/capabilities.rst	Mon Mar 23 10:16:20 2009 -0400
@@ -39,33 +39,33 @@
 
 Commands are also entered into a command history.
 
-  `history` or `hi`
-    List entire command history
+history *or* hi
+  List entire command history
 
-  `list` or `li`
-    List only last command
+list *or* li
+  List only last command
 
-  `hi <N>`
-    List command number <N> from history.  
+hi `<N>`
+  List command number <N> from history.  
 
-  `hi <N>-`, `hi -<N>`
-    List commands from <N> onward, or up to <N>
+hi `<N>-`, hi `-<N>`
+  List commands from <N> onward, or up to <N>
 
-  `hi <str>`
-    Lists commands that include the string <str>
+hi `<str>`
+  Lists commands that include the string <str>
 
-  `hi /<regex>/` 
-    Lists commands that match the regular expression <regex>
+hi `/<regex>/` 
+  Lists commands that match the regular expression <regex>
 
-  `run`, `r`, or `\\g`
-    Run the most recent command again
+run, r, *or* `\\g`
+  Run the most recent command again
 
-  `run <N>`
-    Run command <N>
+run `<N>`
+  Run command <N>
 
-  `run <str>`, `run /<regex>/`
-    Run command matching <str> or <regex> (as for `history`) - 
-    if multiple items would match, run most recent
+run `<str>`, run `/<regex>/`
+  Run command matching <str> or <regex> (as for `history`) - 
+  if multiple items would match, run most recent
 
 Special I/O destinations
 ========================
@@ -125,7 +125,7 @@
 were files in a UNIX filesystem.  Many of the commands also accept flags
 to modify their behavior.
 
-ls {object type/object name, with wildcards}
+ls `{object type/object name, with wildcards}`
   Lists objects from the data dictionaries, as though they were in a 
   *object_type*/*object_name* directory structure.  Thus, `ls view/\*`
   lists all the user's views.  Calling with no argument is equivalent
@@ -141,14 +141,14 @@
   `ls -lt *;10` lists the ten items with the most recent last_ddl_time;
   this can be a good way to answer the question, "What was I working on?"
   
-cat {remainder of query}
-   Shorthand for "SELECT * FROM".  Can be combined with anything else
-   that fits into a SELECT statement (WHERE, ORDER BY, etc.)
+cat `{remainder of query}`
+  Shorthand for "SELECT * FROM".  Can be combined with anything else
+  that fits into a SELECT statement (WHERE, ORDER BY, etc.)
    
-grep {grep {target} {table} [{table2,...}]
-   Equivalent to SELECT * FROM {table} WHERE *any column* LIKE '%{target}%'.
-   Useful when you don't know, don't remember, or don't care which column
-   a value may be found in.
+grep `{target}` `{table}` `[{table2,...}]`
+  Equivalent to SELECT * FROM {table} WHERE *any column* LIKE '%{target}%'.
+  Useful when you don't know, don't remember, or don't care which column
+  a value may be found in.
    
   Options::
   
@@ -161,6 +161,41 @@
   
     -a           Find all objects (not just my own)  
   
+Data dictionary exploration
+===========================
+
+refs `{table_name}`
+  Lists all foreign key constraints on the table or referring to the table.
+  
+deps `{object_name}`
+  Lists all objects dependent upon the named object.
+  
+comments `{table_name}`
+  Prints comments on a table and its columns.
+
+PL/SQL source code
+==================
+
+pull {object_name}
+  Displays the PL/SQL source code for {object_name}.
+  
+  Options:
+    -d, --dump   dump results to files (object_type/object_name.sql)
+    -f, --full   get dependent objects as well
+    -a, --all    all schemas' objects
+  
+bzr, git, hg `{object_name}`
+  Dump source code to files, as `pull -f`, but also creates or commits to a
+  repository of the appropriate distributed version control system
+  (Bazaar, Git, or Mercurial, respectively).  
+  
+find `{target}`
+  Lists all PL/SQL objects whose source code contains the {target} string.  
+  Always case-insensitive.
+  Options::
+
+    -a           Search all PL/SQL objects (not just my own)    
+  
 PostgreSQL-like shortcuts
 =========================
 
@@ -187,31 +222,7 @@
 \\di  _dir_indexes
 \\?   help psql
 ===== ===================
-
-PL/SQL source code
-==================
-
-pull {object_name}
-  Displays the PL/SQL source code for {object_name}.
   
-  Options:
-    -d, --dump   dump results to files (object_type/object_name.sql)
-    -f, --full   get dependent objects as well
-    -a, --all    all schemas' objects
-  
-bzr, git, hg {object_name}
-  Dump source code to files, as `pull -f`, but also creates or commits to a
-  repository of the appropriate distributed version control system
-  (Bazaar, Git, or Mercurial, respectively).  
-  
-find {target}
-  Lists all PL/SQL objects whose source code contains the {target} string.  
-  Always case-insensitive.
-  Options::
-
-    -a           Search all PL/SQL objects (not just my own)    
-  
-
 Bind variables
 ==============
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/comparison.rst	Mon Mar 23 10:16:20 2009 -0400
@@ -0,0 +1,932 @@
+This paper is a comparative guide to sqlpython and
+three other open-source SQL\*Plus alternatives (gqlplus, Senora, YASQL).
+
+******************************************************
+Long Live the Command Line: SQL\*Plus and Alternatives
+******************************************************
+
+`COLLABORATE 09 <http://www.ioug.org/collaborate09/>`_
+
+Talk #405 - Database track
+
+Thursday, May 7, 2009, 11:00 AM - 12:00 PM
+
+Orange County Convention Center West
+
+Orlando, FL
+
+============
+Introduction
+============
+
+Common desktop productivity applications, like spreadsheets, word processors, and the Microsoft Access database, make no clear separation between the program's handling of its underlying data and the interface it provides for user access.  Coming from this world, newcomers to relational databases like Oracle are often confused by the distinction between the database itself and the variety of programs that may be used to access it.  For my first several months of working with Oracle, "Oracle" and "SQL\*Plus" were basically synonyms in my mind.
+
+In reality, there is a great variety of options for accessing data.  End users generally use tools that channel their interaction and insulate them from SQL - tools like Oracle Forms, Reports, and Discoverer; homegrown GUI applications; and web-based interfaces, sometimes built on platforms like Oracle Application Express.
+
+Database administrators and developers need more general-purpose tools that let them see and manipulate data and metadata freely and issue ad-hoc SQL statements, exercising any DML and DDL privileges their accounts have been granted.  GUI tools have gained much attention since the introduction of Oracle SQL Developer, though Quest Software's TOAD remains extremely popular due to its rich feature set and vigorous development.  Other GUI tools include PL/SQL Developer, TORA, and open-source tools like Squirrel.  Oracle Enterprise Manager and 
+JDeveloper can also be used for ad-hoc access to Oracle tables and data.
+
+Nonetheless, command-line interaction through SQL\*Plus remains a cornerstone of most Oracle professionals' work.  SQL\*Plus is ubiquitous and familiar, running on virtually any system.  
+Typing a familiar SQL or SQL\*Plus command is often faster than navigating a GUI's menus.
+SQL\*Plus gives experienced users a sense of full control of their system.  Finally, professionals trying to avoid repetitive motion injuries may find frequent use of a mouse uncomfortable, preferring the ergonomics of a good keyboard.
+
+SQL\*Plus still appears much the same as it did years ago, and it's easy to assume that command-line clients are inherently limited, bare-bones approaches.  There have been improvements, however, which many users are unaware of; by educating yourself about SQL\*Plus' newer features, you may find that SQL\*Plus is a more powerful tool than you'd realized. 
+
+Furthermore, SQL\*Plus is not the only command-line client for interacting with Oracle.  Over the years, Oracle professionals craving greater capabilities have produced several alternate command-line clients that introduce extra features they found useful, and you may as well.  In fact, since all these tools are open-source, you are welcome to add new features to meet your own needs, and to contribute your improvements for incorporation into the tools.  All the SQL\*Plus alternative tools are free of charge.  (SQL\*Plus is also available free of charge, since it is packaged with free Oracle products like the Oracle XE client.)
+
+This paper will begin with a short introduction to each of the tools reviewed.  It will then present enhanced capabilities, most of which appear in more than one of the tools.  Next, it will list the most SQL\*Plus capabilities that some or all of the alternative products lack.  Next, it will cover installing and extending each tool, and finally, briefly discuss cross-RDBMS SQL client tools.
+
+For purposes of this paper, "UNIX" implies UNIX, Linux, and similar POSIX-based operating 
+systems - essentially anything but Windows.  Curly braces (`{}`) denote non-literal values,
+as in `{username}/{password}`.
+
+=====
+Tools
+=====
+
+SQL\*Plus
+---------
+
+Oracle's command-line SQL interface, and possibly the most familiar face of Oracle to the world.  We will take the well-known features of SQL\*Plus as our baseline, assuming that readers are familiar with them.  Some of its lesser-known features, however, will be included in our review of enhanced features.
+
+gqlplus
+-------
+
+This tool is essentially a thin wrapper around SQL\*Plus, passing all input 
+directly to SQL\*Plus.  It preserves virtually all SQL\*Plus' capabilities 
+and feel.  Gqlplus was written in C by Ljubomir J. Buturovic and available under the GNU General 
+Public License.  Version 1.12 was released in December 2006.  It
+is available from its SourceForge page at http://gqlplus.sourceforge.net/.
+
+senora
+------
+
+Senora - an acronym for Shell ENvironment for ORAcle - is a Perl-based tool was created by 
+Martin Drautzburg in 2003.  As its name suggests, its focus is on adding features of the UNIX shell to a SQL\*Plus-like environment.  Version 0.5.1 was released under the
+Artistic License in April 2003.  A few of the capabilities
+described here apply only to the 0.6 release, planned for early 2009; they are labelled as such in the text.
+Senora is available from its SourceForge page at http://senora.sourceforge.net/.
+
+Senora uses a plugin architecture; some of its commands are not available
+in any given senora session until the plugin is activated with the `register` command.  For example, senora's
+tuning-related commands will not be available until `register Tuning` is executed in the session.  
+A startup file, `login.sra`, can be used to automatically register all desired plugins on startup.
+
+YASQL
+-----
+
+Another Perl program, this one by Nathan Shafer and Balint Kozman.  Many of its advanced features center around producing query output in more versatile and convenient forms.  Version 1.83 was released under the GNU General Public License in May 2005.  It is available from its SourceForge page at http://sourceforge.net/projects/yasql/.
+
+sqlpython
+---------
+
+A Python program by Luca Canali and Catherine Devlin (this author).  It imitates the features of senora and YASQL as well as introducing many unique features.  Active development is ongoing, and version 1.6.1 
+was released in March 2009.  Some defects as of this paper's writing (March 2009) are likely to be fixed by the time you read it.  It is available from the Python Package Index at http://pypi.python.org/pypi/sqlpython
+
+====================
+Windows Availability
+====================
+
+========= ===
+gqlplus   no
+senora    yes 
+YASQL     no
+sqlpython yes
+========= ===
+
+Since gqlplus and YASQL were written in C and Perl, respectively, it should be possible,
+in principle, to compile and run them on Windows.  No precompiled versions of them are
+available, however, and no Windows installation procedures have been documented.
+
+All the tools are available for all UNIX environments.
+
+============
+Enhancements
+============
+
+Smart prompt
+------------
+
+*SQL\*Plus (10g and higer), senora, YASQL, sqlpython*
+
+`SQL>`, the standard SQL\*Plus prompt, provides no useful information.  The prompt can be changed with SQL\*Plus' `SET SQLPROMPT` command.
+
+As any DBA who has mistakenly issued a command in what they *believed* was the development instance can tell you, the most useful prompt is one that identifies what username and database is
+being used, like `me@production>`.  To get this prompt in SQL\*Plus, issue `SET SQLPROMPT "_user'@'_connect_identifier> "`.
+
+It's important that the prompt be kept up-to-date; if a `CONNECT` statement is used to connect to a different instance, the prompt must be updated or it will become deceptive.  In SQL\*Plus, `SET SQLPROMPT` should be included in `$ORACLE_HOME/sqlplus/admin/glogin.sql`.  SQL\*Plus 10g and higher execute the commands in `glogin.sql` each time a CONNECT statement changes this information.
+
+SQL\*Plus 9i and earlier run `glogin.sql` only when a new session is started, as does gqlpython.  In these environments, the "smart prompt" cannot be trusted, since a `CONNECT` statement may have changed the reality.
+
+Senora, YASQL, and sqlpython provide this type of smart prompt out of the box.
+
+Tab completion
+--------------
+
+*gqlplus, YASQL, sqlpython*
+
+Tab completion saves typing and avoids spelling errors by filling in the 
+remainder of a command or object name when 
+the first few letters are entered and the TAB key is hit.  If there are multiple valid
+endings to an incomplete word, each potential ending.
+
+Editor choice
+-------------
+
+*all*
+
+It's important to remember to set the `$EDITOR` environment variable before starting any
+command-line tool under UNIX.  All the tools allow textfile editing, but if $EDITOR is 
+not set, they will use the system default editor - generally UNIX `ed`, which can be traumatizing.
+
+sqlpython does not give up so easily if `$EDITOR` is not set, searching for more advanced
+text editors and starting them preferentially.
+
+Scripting
+---------
+
+*all*
+
+All the tools support running scripts with `@path/to/script.sql`.  Establishing a personal 
+library of script files is an important part of building your productivity.  SQL\*Plus,
+gqlplus, and sqlpython also support running scripts from urls, like 
+`@http://host.com/scriptlibrary/myscript.sql`, allowing you to keep your script library
+on the web.
+
+The convenience of the script library is crucial.  If your scripts are hidden away in
+a directory that is difficult to remember and type, you will be tempted to rewrite
+queries instead of using your established scripts.  It's good practice to always start
+your SQL tool in the directory that contains your script library; that way, you can
+access your scripts without having to type path names.  An alias or shortcut at the 
+operating-system level can be useful; for instance, put `alias sql='cd ~/myscriptlibrary; sqlplus'` in your `.bashrc` file.
+
+Choose your script names carefully, so that you will find them easily in the future.  
+To view your script library from within your SQL tool, type `host dir` or `host ls`.
+
+Command history
+---------------
+
+*SQL\*Plus (Windows, or with rlwrap), gqlplus, senora, YASQL, sqlpython*
+
+On Windows, the up- and down-arrow keys can be used to scroll through 
+the history of SQL\*Plus commands issued during the session.  SQL\*Plus on UNIX does not provide this feature.  You can restore the feature, however, by installing a free GNU tool called `rlwrap`, then invoking SQL*\Plus under it: `rlwrap sqlplus me@instance`.
+`rlwrap` can also provide cursor-key history to senora, and in fact to any command-line program. 
+
+Gqlplus, YASQL, and sqlpython provide cursor-key command history out of the box.
+
+In addition, senora and sqlpython have a `history` or `hi` command that gives a numbered list of all commands issued in the session.  
+
+Senora history commands
+~~~~~~~~~~~~~~~~~~~~~~~
+
+================== ===============================================
+hi                 List all commands issued in this session  
+hi {search string} List all commands containing {search string}
+!                  rerun the last command
+!!{N}              rerun command number N
+!{search string}   rerun last command containing {search string}
+================== ===============================================
+
+sqlpython history commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+========================= ==========================================================
+hi                        List all commands issued in this session
+hi {N}                    List command number {N}
+hi -{N}                   List all commands up to command number {N}
+hi {N}-                   List all commands from command number {N} onward
+hi {search string}        List all commands containing {search string}   
+hi /{regex}/              List all commands matching regular expression /{regex}/
+r or \\g                  rerun the last command
+r {N}                     rerun command number {N}
+r {search string}         rerun last command containing {search string}
+r /{regex}/               rerun last command matching {regex}
+========================= ==========================================================
+  
+Neatened output
+---------------
+
+*senora, YASQL, sqlpython*
+
+Senora, YASL, and sqlpython economize on column space when returning query results.  This
+can make output much neater, more compact, and easier to read.  
+
+SQL\*Plus::
+
+  SQL> select * from party where name = 'Gimli';
+  
+  NAME						STR	   INT	      WIS	 DEX	    CON        CHA
+  ---------------------------------------- ---------- ---------- ---------- ---------- ---------- ----------
+  Gimli						 17	    12	       10	  11	     17 	11
+
+YASQL::
+
+  jrrt@orcl> select * from party where name = 'Gimli';
+
+
+  NAME   STR  INT  WIS  DEX  CON  CHA
+  ----- ---- ---- ---- ---- ---- ----
+  Gimli   17   12   10   11   17   11
+
+  1 row selected (0.03 seconds)
+
+UNIX-inspired commands  
+----------------------
+
+*senora, sqlpython*
+
+"Senora" is an acronym for Shell ENvironment for ORAcle, and many of its special features 
+are inspired directly by UNIX shell commands.  `sqlpython` duplicates most of these.  Also, as
+in Unix, these commands can be altered with flags.
+
+ls
+  `ls` is used to list Oracle objects from the data dictionary.  These are reported as though
+  they existed in an {object type}/{object name} directory structure::
+
+    0:jrrt@orcl> ls
+    Table/Party                             Index/Xpk_Party                         
+    
+    0:jrrt@orcl> ls -h
+    NAME
+      ls - list all objects matching pattern
+    
+    SYNOPSIS
+      ls <type/name>
+      -a             List all objects, even with a dollar
+      -l             List validity of objects too
+      -C             List constraints etc too
+      -I             List indexes etc too
+      -i             List invalid objects only
+    
+    FILES
+      DataDictionary.pm
+    0:jrrt@orcl> ls -l Table/*
+    VALID   27-FEB-09               Table/Party
+
+
+cat
+  A shortcut for `SELECT * FROM`.  In fact, you can
+  attach `WHERE` clauses, `ORDER BY`, or other SQL just as if you had typed `SELECT * FROM`.
+
+head [-n N] 
+  Displays the first N rows (default 10) of a table or view.
+  
+grep *target_text* *table to search* [*table 2 to search*, ...]
+  Searches entire tables (all columns) for the desired text::
+  
+      0:jrrt@orcl> grep 17 party
+      
+      Name   |str|int|wis|dex|con|cha|
+      --------------------------------
+      Gimli  | 17| 12| 10| 11| 17| 11|
+      Legolas| 13| 15| 14| 18| 15| 17|
+      
+      2 rows selected.
+      
+Also like UNIX commands, the operation of many senora and sqlpython commands can be altered by
+using flags, as in `grep -i {target text} {table name}` , where `-i` makes the search case-insensitive.
+See `{command} -h` for help on each command, including a list of flags.
+
+Data dictionary access
+----------------------
+
+*senora, YASQL, sqlpython*
+
+Inspecting Oracle objects using the data dictionary requires plenty of typing and an 
+excellent memory.  The alternative tools provide convenient shortcuts.
+
+This table shows some approximately equivalent ways to extract object 
+information from the data dictionary; actually, the SQL queries
+given require considerable interpretation and usually additional joins to get truly
+useful information, whereas the YASQL/senora/sqlpython commands provide information
+ready-to-use.  There are too many possibilities to list, but a sample will give the idea.
+
++------------------------+-----------------+-----------------+----------------------+ 
+|SQL\*Plus               |YASQL            |senora           |sqlpython             |
++========================+=================+=================+======================+
+|SELECT table_name       |show tables      |ls Table/\*      |ls table              |
+|FROM   tabs;            |                 |                 |                      |
++------------------------+-----------------+-----------------+----------------------+ 
+|SELECT object_name,     |                 |                 |ls -l \*a\*           |
+|status, last_ddl_time   |                 |                 |                      |
+|FROM   all_objects      |                 |                 |                      |
+|WHERE  object_name      |                 |                 |                      |
+|LIKE 'A%';              |                 |                 |                      |
++------------------------+-----------------+-----------------+----------------------+
+|SELECT *                |show constraints |refs {table name}|refs {table name}     |
+|FROM   all_constraints  |on {table name}  |                 |                      |
+|WHERE  table_name =     |                 |                 |                      |
+|'{table name}'          |                 |                 |                      |
++------------------------+-----------------+-----------------+----------------------+
+|SELECT *                |show indexes     |                 |\\di {table name}     |
+|FROM   user_indexes     |on {table name}  |                 |                      |
+|WHERE  table_name =     |                 |                 |                      |
+|'{table name}'          |                 |                 |                      |
++------------------------+-----------------+-----------------+----------------------+
+|SELECT *                | show triggers   |                 |                      |
+|FROM   all_triggers     | on {table name} |                 |                      |
+|WHERE  table_name =     |                 |                 |                      |
+|'{table name}'          |                 |                 |                      |
++------------------------+-----------------+-----------------+----------------------+
+|DBMS_METADATA.GET_DDL(  |                 |pull             |pull                  |
+|'{object_type}',        |                 |{object_name}    |{object_name}         |
+|'{object_name}')        |                 |                 |                      |
++------------------------+-----------------+-----------------+----------------------+
+|SELECT * FROM           |                 |desc -l          | comments {table name}|
+|all_tab_comments        |                 |{table_name}     | or \\dd {table name} |
+|WHERE table_name =      |                 |(available in    |                      |
+|'{table name}';         |                 |v0.6)            |                      |
+|SELECT * FROM           |                 |                 |                      |
+|all_col_comments        |                 |                 |                      |
+|WHERE table_name =      |                 |                 |                      |
+|'{table name}';         |                 |                 |                      |
++------------------------+-----------------+-----------------+----------------------+
+|SELECT *                |                 |find {target}    | find -a {target}     |
+|FROM   all_source       |                 |                 |                      |
+|WHERE  text LIKE        |                 |                 |                      |
+|'%{target}%';           |                 |                 |                      |
++------------------------+-----------------+-----------------+----------------------+ 
+|SELECT *                |                 |                 |find -ac {column name}|
+|FROM all_tab_columns    |                 |                 |                      |
+|WHERE column_name LIKE  |                 |                 |                      |
+|'%{column name}%'       |                 |                 |                      |
++------------------------+-----------------+-----------------+----------------------+ 
+
+In sqlpython, the command `ls -tl;10` will list the most recent ten objects by their 
+last DDL time - which can be a handy way to answer the question, "What was I working
+on here?"  (`ls -tl` is similarly useful in the Unix shell.)
+
+convenient viewing of explain plans
+-----------------------------------
+
+*senora, YASQL, sqlpython*
+
+In senora, `xplain {text of query to explain}` is the equivalent of issuing 
+`EXPLAIN PLAN FOR {query}` in SQL\*Plus, then querying the PLAN table with
+spacing inserted for neat formatting.
+
+In YASQL, `show plan` displays the last PLAN table entry, neatly formatted.
+(Issuing `EXPLAIN PLAN` first is up to you.)
+
+In sqlpython, `explain {SQL ID}` shows the execution plan for the SQL statement with the
+given ID.  If SQL ID is omitted, it defaults to the most recent SQL executed.
+(This is not necessarily the last statement `EXPLAIN PLAN` was issued against.)
+
+Special I/O destinations
+------------------------
+
+*YASQL, sqlpython*
+
+You can use the UNIX pipe (`|`) to send query output to a UNIX shell command; this will work much like the pipe in the UNIX shell would.  An example in sqlpython::
+
+  jrrt@orcl> select name from party; | sed 's/Legolas/Elfie-poo/'
+  
+  4 rows selected.
+  
+  NAME   
+  -------
+  Frodo  
+  Gimli  
+  Elfie-poo
+  Sam    
+
+You can use `> {filename}` to route output into a file.
+
+In sqlpython, if the filename is omitted after a `>`, the output is simply redirected into the
+paste buffer, and can then be pasted into a document, email, etc.  An external program called
+`xclip` is necessary to make this work under Unix, and can be installed from your package
+repository.
+
+In YASQL, `<` can be used to import data directly from a CSV file into a table::
+
+  jrrt@orcl> insert into party values (:1, :2, :3, :4, :5, :6, :7); < newmembers.csv
+
+In sqlpython, `< {filename}` simply replaces `< {filename}` with the content of {filename},
+then executes the resulting command.
+
+Special output formats
+----------------------
+
+*SQL\*Plus, YASQL, sqlpython*
+
+SQL\*Plus can produce HTML tables instead of plain text using the `SET MARKUP ON` command.
+Using this, a query could be published as a webpage entirely from within a SQL\*Plus
+session::
+
+  > set markup html on
+  > spool /var/www/myreport.html
+  > select * from mytable;
+  > spool off
+  > set markup html off
+
+YASQL and sqlpython have much more extensive, and convenient, output formatting options.
+By simply replacing the ";" terminating a query with a special backslash sequence, you can
+get output in a variety of alternate formats.
+
+=== ===================================
+\\b bar graph (sqlpython)
+\\c CSV (sqlpython)
+\\c CSV (no column names) (sqlpython)
+\\G list (aligned)
+\\g list (not aligned)
+\\h HTML table (sqlpython)
+\\i as SQL `insert` statements
+\\j JSON (sqlpython)
+\\l line plot, with markers (sqlpython)
+\\l line plot, no markers (sqlpython)
+\\p pie chart (sqlpython)
+\\s CSV
+\\S CSV (no column names)
+\\t transposed (sqlpython)
+\\x XML (sqlpython)
+=== ===================================
+
+Thus, the sqlpython way to produce and web-publish a report fits on a single line::
+
+  > select * from party\h > /var/www/party.html
+
+List (`\\g`) output can be especially useful in reading tables with many columns without confusing
+line wrapping.  Transposed (`\\t`) output is ideal for reading many-columns/few-rows tables
+like V$DATABASE.  Generating plots directly from queries is just fun.
+
+Row output limits
+-----------------
+
+*YASQL, sqlpython*
+
+Follow the terminator in a query (usually ";") with a positive integer, and the query will only
+output up to that number of rows::
+
+  jrrt@orcl> select * from party;2
+
+  NAME  STR INT WIS DEX CON CHA
+  ----- --- --- --- --- --- ---
+  Frodo   8  14  16  15  14  16
+  Gimli  17  12  10  11  17  11
+
+  2 rows selected.
+
+Multiple sessions
+-----------------
+
+*senora, sqlpython*
+
+When `connect -a {username@instance}` is used to create a new connection, senora does not close the
+old connection.  It keeps both connections alive, and switching between them is far more convenient
+than creating new connections::
+
+  Enter sample here
+
+psql-like shortcuts
+-------------------
+
+*YASQL, sqlpython*
+
+Several backslash-character command shortcuts have been copied from psql, the command-line tool
+for the PostgreSQL open-source database, into YASQL and sqlpython.
+
+==== =====================================
+\\c  connect (sqlpython)
+\\c  clear buffer (YASQL)
+\\d  describe (sqlpython)
+\\e  edit
+\\g  run (sqlpython)
+\\h  help (sqlpython)
+\\i  load (sqlpython)
+\\l  list (YASQL)
+\\o  spool (sqlpython)
+\\p  list
+\\q  quit
+\\w  save
+\\db directory of tablespaces (sqlpython)
+\\dd comments (sqlpython)
+\\dn directory of schemas (sqlpython)
+\\dt directory of tables
+\\dv directory of views
+\\di directory of indexes
+==== =====================================
+
+Enhanced bind variables
+-----------------------
+
+*sqlpython*
+
+Senora and sqlpython support SQL\*Plus-style bind variables.  sqlpython also has an optional, easygoing
+syntax for defining bind variables and permits them to be dynamically defined (no declaration required)::
+
+  jrrt@orcl> :name = 'Legolas'
+  jrrt@orcl> print
+  :name = Legolas
+
+sqlpython's `bind` command creates and populates bind variables for each
+column of the row most recently returned from a query.  The optional `autobind` 
+setting automatically does this after every query that returns a single row.
+
+::
+
+  jrrt@orcl> select name, str, int from party where name = :name;
+
+  NAME    STR INT
+  ------- --- ---
+  Legolas  13  15
+
+  1 row selected.
+
+  jrrt@orcl> print
+  :1 = Legolas
+  :2 = 13
+  :3 = 15
+  :int = 15
+  :name = Legolas
+  :str = 13
+
+Tuning
+------
+
+*senora, sqlpython*
+
+Each program provides an assortment of commands to provide performance-related data.  
+There are too many commands for detailed description, but a partial list will supply
+some idea for the capabilities.  
+
+Senora tuning commands
+~~~~~~~~~~~~~~~~~~~~~~
+
+============== ====== ==========================================================
+command        abbrev effect
+============== ====== ==========================================================
+analyzeObject  ana    analyze tables or indexes
+cacheStats     cst    Display memory statisticts
+compareStat    coms   compare statistics of two schemas
+cstatement     cs     lists current SQL statements, by session
+cstatement -S  cs -S  lists current SQL statements with their execution stats
+FkeyIndexes    fki    analyze the existance of foreign key indexes
+hwm                   get high watermark info on analyzed tables (experimental)
+jobs                  print job information
+kept                  show kept (pinned) code
+kill                  kill a session
+locks                 show sessions and the objects the are waiting for
+logs                  print redo log activity
+profile               estimate current execution times
+ps                    print session information
+resize                resize datafiles 
+rollSegs              print rollback info
+show parameter sp     show init.ora parameter
+space                 show tablespace and file stats
+stat                  print session statistics
+validate       vi     xvalidate structure an index
+waits                 show what sessions are waiting for
+xqueries       xq     show most expensive quieries
+============== ====== ==========================================================
+
+Before senora tuning commands can be run, `register Tuning` must be executed in the session.
+
+sqlython tuning commands
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+======== =====================================================
+load     prints the OS load on all cluster nodes (10g RAC)
+longops  prints from gv$session_longops
+sessinfo prints session information. Parameter: sid
+sql      prints the sql text from the cache. Parameter: sql_id
+top      List top active sessions
+top9i    9i (and single instance) version of top
+======== =====================================================
+
+Wildcards in column names
+-------------------------
+
+*YASQL, sqlpython*
+
+YASQL supports `\*` wildcards in column names, if there is a ~/.yasqlrc or /etc/yasql.conf file containing
+`column_wildcards=1`.
+
+sqlpython's wildcards are more powerful.  When the option wildsql is set to ON, 
+sqlpython will accept these in the column list of a SELECT statement:
+
+  * Wildcards (`\*` or `%`, and `?`)
+
+  * Column numbers (`#1, #2`)
+
+  * !, meaning NOT.  `!str` means "all columns except STR".
+
+These can be combined.  `SELECT !#2, !c% FROM party` means 'select all columns except column #2 and any column beginning with 'C'", and is translated by sqlpython into `SELECT name, int, wis, dex FROM party;`.
+
+====================
+Drawbacks and quirks
+====================
+
+No tool reproduces all the features of SQL\*Plus (though gqlplus comes very, very close).  These are the drawbacks most likely to be noticed.
+
+Smart prompt
+------------
+
+Like SQL\*Plus, gqlplus invokes glogin.sql when it is started.  Unfortunately, gqlplus does not re-invoke it when a new CONNECT statement is issued during a session.  Thus, as for SQL\*Plus versions xxxx and before, the "smart prompt" can become dangerously deceptive in gqlplus.
+
+PL/SQL
+------
+
+Gqlplus, Senora, and sqlpython can run all PL/SQL as SQL\*Plus does.  
+
+YASQL can run single lines of PL/SQL with `exec`, and automatically echoes DBMS_OUTPUT.PUT_LINE output, but it does not recognize anonymous SQL BEGIN..END blocks.  sqlpython requires that PL/SQL blocks be bracketed
+in `REMARK BEGIN` and `REMARK END` statements
+
+Executing shell commands
+------------------------
+
+In SQL\*Plus, `host {command}` or `!{command}` run {command} on the operating system.
+These work in all the alternative tools as well, except that senora recognizes only 
+`host` (since, in senora, `!` is used for re-running commands from the history instead.)
+
+Error messages
+--------------
+
+Error messages in some programs are less friendly than in SQL*\Plus.  For instance, senora
+responds to most errors with "what ?"::
+
+  0:jrrt@orcl> grep;
+  what ? "grep;" ?
+  
+sqlpython passes Python or Oracle errors it encounters up to the user, but it cannot
+highlight the place in a SQL command where a syntax error took place.
+
+When compiling PL/SQL objects (functions, packages, procedures) that contain errors, 
+the tools vary in the quality of information they return.
+ 
+* Senora reports that a compilation error has occured, and `show errors` works as expected.
+      
+* YASQL reports that a compilation error has occured.  `show errors` in YASQL lists the compilation errors for *all* invalid PL/SQL objects in the schema.
+  
+* sqlpython reports errors immediately upon compilation.
+      
+Note that, in senora and YASQL, abbreviated forms of `show errors`, like `sho err`, are not recognized.
+
+Startup
+-------
+
+Only SQL\*Plus can be used to login to a closed database.  gqlplus hangs during the attempt.
+The other tools simply report that the database is closed and cannot connect.
+
+Text file editing
+-----------------
+
+In senora and YASQL, the `ed` and `\\e` commands (respectively) can be used to edit
+text files using the environment's default editor.  Unlike SQL\*Plus, however, they
+do not edit the *most recently issued command* (the buffer), and the resulting file
+is not automatically run.  sqlpython's `ed`, without an argument, edits the SQL buffer 
+as SQL\*Plus does, and runs the result when the editor is closed.  You may also 
+`ed {filename}` to edit a specific text file, `ed {N}` to load command number {N} 
+from the command history into the buffer and edit it.  Finally, if your EDITOR 
+environment is not set, sqlpython tries to find a more user-friendly editing program 
+on your machine than UNIX ed.
+
+Hints
+-----
+
+sqlpython removes all comments from commands at an early stage of parsing, and thus 
+will not honor execution hints embedded within SQL statements.
+
+Variables
+---------
+
+YASQL does not support bind variables (`:` variables) or substitution variables (`&` variables).
+The other tools support them, and sqlpython has enhanced bind variables (see above).
+
+Parsing speed
+-------------
+
+sqlpython parses each line of a SQL command as it is entered to determine whether the command
+is finished yet.  This produces noticable waits as a query approaches ten lines of SQL, and
+downright annoying as it grows beyond that.  To avoid it, you can prefix long queries with
+`REMARK BEGIN` and end them with `REMARK END`, promising sqlpython that you will only enclose a
+single query and freeing it from the need to parse until `REMARK END` is reached.
+
+Maturity
+--------
+
+All the alternate tools are produced by individuals or very small groups, so it is not
+uncommon to encounter bugs.  If you encounter one, you can e-mail the author; 
+for sqlpython in particular, error reports filed in the project's bug tracker at 
+http://trac-hg.assembla.com/sqlpython are appreciated.  Since the projects are
+open-source, and most of them are written in the easy-to-use languages Perl and Python, 
+you can always fix bugs yourself and improve the products for everyone.
+
+============
+Installation
+============
+
+First, make sure you have Oracle client software on your machine!  gqlplus simply 
+wraps SQL\*Plus, and wenora, YASQL, and
+sqlpython depend on 
+Perl or Python modules that access the Oracle OCI, so none of them will work in the absence of
+an Oracle client.
+
+You will also need Perl (for senora or YASQL) or Python (for sqlpython).  Virtually all UNIX
+systems will have these languages installed, and frankly, every computer *should*.  Free installers for
+all common platforms are available from the languages' websites at http://www.perl.org/  
+and http://python.org/.
+
+gqlplus
+-------
+
+Gqlplus is distributed as a gzipped tarball, downloadable from http://gqlplus.sourceforge.net/,
+containing the C source code and a precompiled binary
+for Linux.  To use on Linux, simply expand the tarball and run Linux/gqlplus; you may want to place
+the file or a link to it somewhere in your PATH.  On other systems, you will a C compiler such 
+as `gcc`; run `./configure` and `make` as directed in the project's `README` file.
+
+Senora
+------
+
+Unix
+~~~~
+
+Senora is distributed as a gzipped tarball, downloadable from http://senora.sourceforge.net/
+
+Senora depends on the DBI and DBD::Oracle packages.  Perl's `cpan` tool is a fairly convenient way 
+to install them::
+
+  $ cpan
+  
+  cpan> install DBI 
+  cpan> force install DBD::Oracle
+
+If this is your first time running `cpan`, it will ask you many configuration questions
+on startup.  Accepting the default answers is generally correct.  
+Running under `sudo`, `cpan` may not find your $ORACLE_HOME; running as `root` avoids this problem.
+
+After installation is complete, download and unzip Senora-0.5.1.tgz from http://senora.sourceforge.net/, cd into senora/core, and run `perl Senora.pm`.
+
+Windows
+~~~~~~~
+
+http://senora.sourceforge.net/ also has a "Senora for Windows" download.  Download it, unzip, and
+run `SenoraForWindows/senora.exe`.  You may wish to create a shortcut for your convenience.
+
+YASQL
+-----
+
+Like senora, YASQL depends on the DBI and DBD::Oracle packages.  Several other Perl packages
+are recommended and can also be installed through `cpan`::
+
+  cpan> install DBI 
+  cpan> force install DBD::Oracle
+  cpan> install Term::ReadKey Text::CSV_XS Time::HiRes 
+
+Download yasql-1.83.tar.gz from http://sourceforge.net/projects/yasql/, unzip and untar it, and
+cd into yasql-1.83.  Run `./configure`, `make`, and (as root) `make install`.
+This will place the yasql executable into /usr/local/bin, so that you can run it from anywhere
+by simply typing `yasql`.
+
+sqlpython
+---------
+
+Some UNIX machines come shipped with Python but without python-dev, which you should install
+from your distribution's package repository if you don't have it already.  Downloads from http://python.org
+(including the Windows installer) include python-dev out of the box.
+
+The easiest way to install sqlpython is with `easy_install`, a popular Python package installation
+tool.  You can get easy_install by installing python-setuptools from your Linux distribution's repository,
+or by downloading directly from http://peak.telecommunity.com/DevCenter/EasyInstall.
+
+Once you have easy_install, simply type (as root, or using `sudo`)::
+
+  $ easy_install -UZ sqlpython
+
+The -UZ flags, though optional, will update your sqlpython installation, if necessary, and 
+unzip the code so that you can modify it.
+
+To generate graphs using sqlpython's `\\b`, `\\g`, and `\\l` terminators, you will need to install `pylab`::
+
+  $ easy_install pylab
+
+===================
+Extending the tools 
+===================
+
+All the alternative tools are open-source, so you have the right to view the source
+code, make your own modifications, contribute your modifications back to the original
+project, and even distribute your own modified version.  YASQL, senora, and sqlpython
+are especially easy to modify since they are written in dynamic languages, which are
+easy to program in and require no compilation step.
+
+When you are ready to begin customizing or improving your favorite tool, imitation is
+the key; look in the source code files of the tool for the code corresponding to a 
+simple command, copy and rename it, then work on small modifications until the new
+functionality meets your needs.
+
+As a specific example, we will add new commands to sqlpython.  
+
+`easy_install -UZ sqlpython` places sqlpython's source code files into your Python site-packages
+directory, probably someplace like `/usr/lib/python2.5/site-packages/`.  You can edit the files there
+(you may need to change their ownership from `root` to yourself first).  Any method in 
+`mysqlpy` whose name begins with `do_` will be registered as a sqlpython
+command the next time sqlpython is started.  The simplest possible new command could
+be written as::
+
+    def do_hello(self, arg):
+        print 'Hello, World!'
+        
+Now, typing `hello [arguments]` in a sqlpython session calls `do_hello`.  The
+optional arguments will be assigned to `arg`, but in this case they are not used.
+Let's see a more useful function, one that will
+
+* Make use of the argument string
+  
+* Provide online documentation
+  
+* Use a flag to optionally modify the command's behavior
+  
+* Send output to file, paste buffer, or pipe when `>` or `|` is used.
+  
+::
+
+    @options([make_option('-u', '--uppercase', action='store_true', 
+                          help='use ALL CAPS')])
+    def do_greet(self, arg, opts):
+        'Provides a personalized greeting.'
+        result = 'Hello %s!\n' % arg
+        if opts.uppercase:
+            result = result.upper()
+        self.stdout.write(result)
+  
+Now we run sqlpython and try the new command::
+
+    jrrt@orcl> greet
+    Hello !
+    jrrt@orcl> greet Larry E.
+    Hello Larry E.!
+    jrrt@orcl> help greet
+    Provides a personalized greeting.
+    Usage: greet [options] arg
+    
+    Options:
+      -h, --help       show this help message and exit
+      -u, --uppercase  use ALL CAPS
+    
+    jrrt@orcl> greet -u World
+    HELLO WORLD!
+
+Senora v0.6 includes a command, `lregister`, that will automatically create a new senora plugin from
+SQL commands in a file.  This will make an incredibly easy way to make your own senora 
+commands for your favorite tasks.
+
+=================
+Cross-RDBMS tools
+=================
+
+All the tools reviewed so far have been designed purely for Oracle.  If you work with
+multiple RDBMS platforms, however, you may be interested in a SQL tool that is compatible
+with all of them, which opens up a whole new set of possible programs.
+
+One such possibility is sqlcmd (http://www.clapper.org/software/python/sqlcmd),
+an open-source Python program available without charge on all platforms.  It
+operates seamlessly across Oracle, MySQL, PostreSQL, SQLite, and MS SQL Server.
+
+Its functionality is rather basic compared to the Oracle-specific tools, and there
+are conventions that will be unfamiliar to those immersed in an Oracle world.  For
+example, use of stored procedures is not supported at all, column and table names
+are case-sensitive, and database connection details are specified with an unfamiliar
+format (`sqlcmd -d orcl,oracle,localhost,scott,tiger` in place of `sqlplus scott/tiger@orcl`).  Nonetheless, it and similar
+tools are viable options when cross-RDBMS compatibility is a key requirement.
+    
+=======    
+Summary
+=======
+
+All the tools offer capabilities that will make command-line interaction with Oracle easier and 
+more powerful, and you should experiment to find out which one(s) suit you best.  If you want as
+little change as possible, gqlplus provides a few extra features while being almost completely
+transparent and SQL\*Plus-compatible.  Sqlpython has the broadest set of features and is
+being developed most actively.  YASQL and senora may be good alternatives if you want some 
+of the features unique to those programs, such as senora's rich set of tuning commands, 
+or if you prefer Perl for writing your own improvements.  
+
+There will always be a place for SQL\*Plus, but alternative tools
+can replace and improve upon some of the work you have been doing both with SQL\*Plus
+and with GUI tools.  No matter which programs you use, you will get more powerful, 
+enjoyable commmand-line experiences and gain new appreciation for what open-source 
+development can bring to an Oracle environment.
+
+=====
+Links
+=====
+
+* http://gqlplus.sourceforge.net/
+
+* http://senora.sourceforge.net/
+
+* http://sourceforge.net/projects/yasql/
+
+* http://pypi.python.org/pypi/sqlpython
+
+* http://www.clapper.org/software/python/sqlcmd
+
+* http://catherinedevlin.blogspot.com/
+
+================
+Acknowledgements
+================
+
+Thanks to all the authors of the open-source tools reviewed here, but especially to Martin Drautzburg,
+author of senora, for important corrections and additions.
--- a/docs/source/index.rst	Mon Mar 23 08:36:02 2009 -0400
+++ b/docs/source/index.rst	Mon Mar 23 10:16:20 2009 -0400
@@ -13,6 +13,7 @@
    intro
    capabilities
    limitations
+   comparison
 
 
 Indices and tables
--- a/docs/source/limitations.rst	Mon Mar 23 08:36:02 2009 -0400
+++ b/docs/source/limitations.rst	Mon Mar 23 10:16:20 2009 -0400
@@ -2,6 +2,9 @@
 Limitations
 ===========
 
+Bug reports and feature requests are welcome at 
+`the project's bug tracker on Assembla <http://trac-hg.assembla.com/sqlpython/report>`_.
+
 Slow parsing
 ------------
 
@@ -26,5 +29,8 @@
 Unsupported commands
 --------------------
 
-  * DBMS_OUTPUT.PUT_LINE
+* PASSWORD
 
+* SET FEEDBACK OFF
+
+* SET AUTOTRACE ON
\ No newline at end of file
--- a/sqlpython/sqlpyPlus.py	Mon Mar 23 08:36:02 2009 -0400
+++ b/sqlpython/sqlpyPlus.py	Mon Mar 23 10:16:20 2009 -0400
@@ -33,7 +33,30 @@
     pass
 
 descQueries = {
-'TABLE': ("""
+'TABLE': {
+    True: # long description
+("""
+SELECT atc.column_id "#",
+atc.column_name,
+CASE atc.nullable WHEN 'Y' THEN 'NULL' ELSE 'NOT NULL' END "Null?",
+atc.data_type ||
+CASE atc.data_type WHEN 'DATE' THEN ''
+ELSE '(' ||
+CASE atc.data_type WHEN 'NUMBER' THEN TO_CHAR(atc.data_precision) ||
+CASE atc.data_scale WHEN 0 THEN ''
+ELSE ',' || TO_CHAR(atc.data_scale) END
+ELSE TO_CHAR(atc.data_length) END 
+END ||
+CASE atc.data_type WHEN 'DATE' THEN '' ELSE ')' END
+data_type,
+acc.comments
+FROM all_tab_columns atc
+JOIN all_col_comments acc ON (acc.owner = atc.owner AND acc.table_name = atc.table_name AND acc.column_name = atc.column_name)
+WHERE atc.table_name = :object_name
+AND      atc.owner = :owner
+ORDER BY atc.column_id;""",),
+    None: # short description
+("""
 SELECT atc.column_name,
 CASE atc.nullable WHEN 'Y' THEN 'NULL' ELSE 'NOT NULL' END "Null?",
 atc.data_type ||
@@ -49,7 +72,10 @@
 FROM all_tab_columns atc
 WHERE atc.table_name = :object_name
 AND      atc.owner = :owner
-ORDER BY atc.column_id;""",),
+ORDER BY atc.column_id;""",)
+}
+          ,
+
 'PROCEDURE': ("""
 SELECT NVL(argument_name, 'Return Value') argument_name,             
 data_type,
@@ -371,12 +397,22 @@
             self.resultset.pystate['binds'][colname] = self[idx]
             self.resultset.pystate['binds'][str(idx+1)] = self[idx]
               
+def centeredSlice(lst, center=0, width=1):
+    width = max(width, -1)
+    if center < 0:
+        end = center + width + 1
+        if end >= 0:
+            end = None
+        return lst[center-width:end]
+    else:
+        return lst[max(center-width,0):center+width+1] 
+    
 class sqlpyPlus(sqlpython.sqlpython):
     defaultExtension = 'sql'
     abbrev = True    
     sqlpython.sqlpython.shortcuts.update({':': 'setbind', 
                                           '\\': 'psql', 
-                                          '@': '_load'})
+                                          '@': 'get'})
     multilineCommands = '''select insert update delete tselect
                       create drop alter _multiline_comment'''.split()
     sqlpython.sqlpython.noSpecialParse.append('spool')
@@ -806,7 +842,16 @@
                         ddl = [[object_type, object_name, owner]]
                     for ddlargs in ddl:
                         try:
-                            self.stdout.write('REMARK BEGIN %s\n%s\nREMARK END\n\n' % (object_name, str(self.curs.callfunc('DBMS_METADATA.GET_DDL', cx_Oracle.CLOB, ddlargs))))
+                            code = str(self.curs.callfunc('DBMS_METADATA.GET_DDL', cx_Oracle.CLOB, ddlargs))
+                            if opts.lines:
+                                code = code.splitlines()
+                                template = "%%-%dd:%%s" % len(str(len(code)))
+                                code = '\n'.join(template % (n+1, line) for (n, line) in enumerate(code))
+                            if opts.num is not None:
+                                code = code.splitlines()
+                                code = centeredSlice(code, center=opts.num+1, width=opts.width)
+                                code = '\n'.join(code)
+                            self.stdout.write('REMARK BEGIN %s\n%s\nREMARK END\n\n' % (object_name, code))
                         except cx_Oracle.DatabaseError, errmsg:
                             if object_type == 'JOB':
                                 print '%s: DBMS_METADATA.GET_DDL does not support JOBs (MetaLink DocID 567504.1)' % object_name
@@ -864,6 +909,10 @@
             
     @options([make_option('-d', '--dump', action='store_true', help='dump results to files'),
               make_option('-f', '--full', action='store_true', help='get dependent objects as well'),
+              make_option('-l', '--lines', action='store_true', help='print line numbers'),
+              make_option('-n', '--num', type='int', help='only code near line #num'),
+              make_option('-w', '--width', type='int', default=5, 
+                          help='# of lines before and after --lineNo'),              
               make_option('-a', '--all', action='store_true', help="all schemas' objects"),
               make_option('-x', '--exact', action='store_true', help="match object name exactly")])
     def do_pull(self, arg, opts):
@@ -940,38 +989,51 @@
             sql = "SELECT * from %s_source where UPPER(text) like '%%%s%%';" % (opts.scope['view'], capArg)
         self.do_select(self.parsed(sql, terminator=arg.parsed.terminator or ';', suffix=arg.parsed.suffix))
         
-    @options([all_users_option])
+            
+    @options([all_users_option,
+              make_option('-l', '--long', action='store_true', help='include column #, comments')])
     def do_describe(self, arg, opts):
         "emulates SQL*Plus's DESCRIBE"
         target = arg.upper()
-        if not target:
-            return self.do_select(self.parsed("""SELECT object_name, object_type%s 
-                                                 FROM   %s_objects 
-                                                 WHERE  object_type IN ('TABLE','VIEW','INDEX') 
-                                                 ORDER BY object_name;""" % (opts.scope['col'], opts.scope['view']),
-                                              terminator=arg.parsed.terminator or ';', suffix=arg.parsed.suffix))
-        object_type, owner, object_name = self.resolve(target)
-        if not object_type:
-            return self.do_select(self.parsed("""SELECT object_name, object_type%s FROM %s_objects
-                                                 WHERE  object_type IN ('TABLE','VIEW','INDEX')
-                                                 AND    object_name LIKE '%%%s%%'
-                                                 ORDER BY object_name;""" %
-                                              (opts.scope['col'], opts.scope['view'], target),
-                                              terminator=arg.parsed.terminator or ';', suffix=arg.parsed.suffix))
+        objnameclause = ''
+        if target:
+            objnameclause = "AND    object_name LIKE '%%%s%%' " % target
+            object_type, owner, object_name = self.resolve(target)
+        if (not target) or (not object_type):
+            if opts.long:
+                query =  """SELECT o.object_name, o.object_type, o.owner, c.comments
+                            FROM   all_objects o
+                            LEFT OUTER JOIN all_tab_comments c ON (o.owner = c.owner AND o.object_name = c.table_name AND o.object_type = 'TABLE')
+                            WHERE  object_type IN ('TABLE','VIEW','INDEX') 
+                            %sORDER BY object_name;""" % objnameclause
+            else:
+                query =  """SELECT object_name, object_type%s 
+                            FROM   %s_objects 
+                            WHERE  object_type IN ('TABLE','VIEW','INDEX') 
+                            %sORDER BY object_name;""" % \
+                            (opts.scope['col'], opts.scope['view'], objnameclause)                                                                             
+            return self.do_select(self.parsed(query, terminator=arg.parsed.terminator or ';', 
+                                  suffix=arg.parsed.suffix))
         self.stdout.write("%s %s.%s\n" % (object_type, owner, object_name))
-        descQ = descQueries.get(object_type)
-        if descQ:
+        try:
+            if object_type == 'TABLE':
+                descQ = descQueries[object_type][opts.long]
+            else:
+                descQ = descQueries[object_type][opts.long]                
             for q in descQ:
                 self.do_select(self.parsed(q, terminator=arg.parsed.terminator or ';' , suffix=arg.parsed.suffix), 
-                               bindVarsIn={'object_name':object_name, 'owner':owner})
-        elif object_type == 'PACKAGE':
-            packageContents = self.select_scalar_list(descQueries['PackageObjects'][0], {'package_name':object_name, 'owner':owner})
-            for packageObj_name in packageContents:
-                self.stdout.write('Arguments to %s\n' % (packageObj_name))
-                sql = self.parsed(descQueries['PackageObjArgs'][0], terminator=arg.parsed.terminator or ';', suffix=arg.parsed.suffix)
-                self.do_select(sql, bindVarsIn={'package_name':object_name, 'owner':owner, 'object_name':packageObj_name})
+                               bindVarsIn={'object_name':object_name, 'owner':owner})            
+        except KeyError:
+            if object_type == 'PACKAGE':
+                packageContents = self.select_scalar_list(descQueries['PackageObjects'][0], {'package_name':object_name, 'owner':owner})
+                for packageObj_name in packageContents:
+                    self.stdout.write('Arguments to %s\n' % (packageObj_name))
+                    sql = self.parsed(descQueries['PackageObjArgs'][0], terminator=arg.parsed.terminator or ';', suffix=arg.parsed.suffix)
+                    self.do_select(sql, bindVarsIn={'package_name':object_name, 'owner':owner, 'object_name':packageObj_name})
+
 
     def do_deps(self, arg):
+        '''Lists all objects that are dependent upon the object.'''
         target = arg.upper()
         object_type, owner, object_name = self.resolve(target)
         if object_type == 'PACKAGE BODY':
@@ -1387,8 +1449,10 @@
             print sql
         self.curs.execute(sql, bindvars)
 
+    #@options([make_option('-l', '--long', action='store_true', 
+    #                      help='Wordy, easy-to-understand form'),])                
     def do_refs(self, arg):
-        '''Lists referential integrity (foreign key constraints) on an object.'''
+        '''Lists referential integrity (foreign key constraints) on an object or referring to it.'''
         
         if not arg.strip():
             print 'Usage: refs (table name)'