1
0
Fork 0

Autorefresh for \c, update readme with thanks.

This commit is contained in:
Amjith Ramanujam 2014-12-08 00:43:21 -08:00
parent a88192bc0c
commit 60cbbdcb28
6 changed files with 48 additions and 22 deletions

View File

@ -124,3 +124,20 @@ Then you can install pgcli:
$ sudo pip install pgcli
Gratitude:
==========
A special thanks to Jonathan Slenders for creating Python Prompt Toolkit, which
is quite literally the backbone library that made this app possible. Jonathan
has also provided valuable feedback and support during the development of this
app.
This app also includes tabulate (https://pypi.python.org/pypi/tabulate) library
for printing the output of the tables. The reason for copying it directly and
not listing it as a dependency is because I had to make a change to the table
format which is merged back into the original repo, but not yet released in
PyPI.
Click is used for command line option parsing and printing error messages.
Thanks to psycopg2 for providing a rock solid interface to Postgres dataabase.

13
TODO
View File

@ -1,9 +1,10 @@
* [] Vendor in tabulate.
* [] Create a separate pgspecial package.
* [] Vendor in pgspecial package.
* [] Add \c command.
* [] Add MySQL commands (use, describe etc)
* [X] Add exit keyword.
* [X] Vendor in tabulate.
* [X] Create a separate pgspecial package.
* [X] Vendor in pgspecial package.
* [X] Auto-refresh for \c and 'use' statements.
* [X] Add \c command.
* [O] Add MySQL commands (use, describe etc)
* [X] Add exit, quit and \q.
* [] Show only table sensitive columns.
* [] Add logging.
* [] Add some tests. Sanity, Unit, Completion, Config.

View File

@ -12,9 +12,10 @@ from prompt_toolkit.layout.prompt import DefaultPrompt
from prompt_toolkit.layout.menus import CompletionsMenu
from prompt_toolkit.history import FileHistory
from pygments.lexers.sql import SqlLexer
from tabulate import tabulate
import sqlparse
from .packages.tabulate import tabulate
from .packages.pgspecial import COMMANDS
from .pgcompleter import PGCompleter
from .pgstyle import PGStyle
from .pgexecute import PGExecute
@ -50,7 +51,7 @@ def cli(database, user, password, host, port):
menus=[CompletionsMenu()],
lexer=SqlLexer)
completer = PGCompleter(config.getboolean('main', 'smart_completion'))
completer.extend_special_commands(pgexecute.special_commands.keys())
completer.extend_special_commands(COMMANDS.keys())
completer.extend_table_names(pgexecute.tables())
completer.extend_column_names(pgexecute.all_columns())
line = PGLine(completer=completer,
@ -66,7 +67,8 @@ def cli(database, user, password, host, port):
# because we want to raise the Exit exception which will be caught
# by the try/except block that wraps the pgexecute.run() statement.
if (document.text.strip().lower() == 'exit'
or document.text.strip().lower() == 'quit'):
or document.text.strip().lower() == 'quit'
or document.text.strip() == '\q'):
raise Exit
try:
rows, headers, status = pgexecute.run(document.text)
@ -78,11 +80,12 @@ def cli(database, user, password, host, port):
# Refresh the table names and column names if necessary.
if need_completion_refresh(document.text):
completer.reset_completions()
completer.extend_table_names(pgexecute.tables())
completer.extend_column_names(pgexecute.all_columns())
except Exit:
print ('GoodBye!')
def need_completion_refresh(sql):
parsed = sqlparse.parse(sql)
return parsed and parsed[0].token_first().value in ('alter', 'create')
first_token = sql.split()[0]
return first_token in ('alter', 'create', 'use', '\c', 'drop')

View File

@ -201,7 +201,6 @@ def describe_one_table_details(cur, schema_name, relation_name, oid, verbose):
cell.append(modifier)
#import pdb; pdb.set_trace()
# Sequence
if tableinfo.relkind == 'S':
cell.append(seq_values[i])
@ -693,7 +692,6 @@ def execute(cur, command, verbose, arg):
global COMMANDS
command_executor = COMMANDS[command]
import pdb; pdb.set_trace()
if callable(command_executor):
return command_executor(cur, arg, verbose)
elif isinstance(command_executor, str):

View File

@ -46,6 +46,11 @@ class PGCompleter(Completer):
self.column_names.extend(column_names)
self.all_completions.update(column_names)
def reset_completions(self):
self.table_names = []
self.column_names = ['*']
self.all_completions = set(self.keywords)
@staticmethod
def find_matches(text, collection):
for item in collection:

View File

@ -1,14 +1,16 @@
import psycopg2
from .packages import pgspecial
class PGExecute(object):
special_commands = {
'\d': '''SELECT n.nspname as "Schema", c.relname as "Name", CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign table' END as "Type", pg_catalog.pg_get_userbyid(c.relowner) as "Owner" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r','v','m','S','f','') AND n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema' AND n.nspname !~ '^pg_toast' AND pg_catalog.pg_table_is_visible(c.oid) ORDER BY 1,2;''',
'\dt': '''SELECT n.nspname as "Schema", c.relname as "Name", CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign table' END as "Type", pg_catalog.pg_get_userbyid(c.relowner) as "Owner" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r','') AND n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema' AND n.nspname !~ '^pg_toast' AND pg_catalog.pg_table_is_visible(c.oid) ORDER BY 1,2;'''
}
tables_query = '''SELECT c.relname as "Name" FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE
c.relkind IN ('r','') AND n.nspname <> 'pg_catalog' AND n.nspname <>
'information_schema' AND n.nspname !~ '^pg_toast' AND
pg_catalog.pg_table_is_visible(c.oid) ORDER BY 1;'''
tables_query = '''SELECT c.relname as "Name" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r','') AND n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema' AND n.nspname !~ '^pg_toast' AND pg_catalog.pg_table_is_visible(c.oid) ORDER BY 1;'''
columns_query = '''SELECT column_name FROM information_schema.columns WHERE table_name =%s;'''
columns_query = '''SELECT column_name FROM information_schema.columns WHERE
table_name =%s;'''
def __init__(self, database, user, password, host, port):
self.conn = psycopg2.connect(database=database, user=user,
@ -45,9 +47,9 @@ class PGExecute(object):
'user "%s"' % (self.dbname, self.user))
with self.conn.cursor() as cur:
if sql in self.special_commands:
cur.execute(self.special_commands[sql])
else:
try:
return pgspecial.execute(cur, *self.parse_pattern(sql))
except KeyError:
cur.execute(sql)
# cur.description will be None for operations that do not return