diff options
author | Amjith Ramanujam <amjith@newrelic.com> | 2014-12-08 00:43:21 -0800 |
---|---|---|
committer | Amjith Ramanujam <amjith@newrelic.com> | 2014-12-08 00:43:21 -0800 |
commit | 60cbbdcb28a76daa8d6bff380ed44c29b79b3360 (patch) | |
tree | 7d24dddabf67080beffb0b6e38f09b248fa27c09 | |
parent | a88192bc0cd3c5d70ed253e1c71eed63ea66681c (diff) |
Autorefresh for \c, update readme with thanks.
-rw-r--r-- | README.rst | 17 | ||||
-rw-r--r-- | TODO | 13 | ||||
-rwxr-xr-x | pgcli/main.py | 13 | ||||
-rw-r--r-- | pgcli/packages/pgspecial.py | 2 | ||||
-rw-r--r-- | pgcli/pgcompleter.py | 5 | ||||
-rw-r--r-- | pgcli/pgexecute.py | 20 |
6 files changed, 48 insertions, 22 deletions
@@ -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. @@ -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. diff --git a/pgcli/main.py b/pgcli/main.py index cec004a2..d60b34e8 100755 --- a/pgcli/main.py +++ b/pgcli/main.py @@ -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') diff --git a/pgcli/packages/pgspecial.py b/pgcli/packages/pgspecial.py index dde728a8..2d704f47 100644 --- a/pgcli/packages/pgspecial.py +++ b/pgcli/packages/pgspecial.py @@ -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): diff --git a/pgcli/pgcompleter.py b/pgcli/pgcompleter.py index ccaf6086..267f196f 100644 --- a/pgcli/pgcompleter.py +++ b/pgcli/pgcompleter.py @@ -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: diff --git a/pgcli/pgexecute.py b/pgcli/pgexecute.py index b0e8654d..10a1e1ba 100644 --- a/pgcli/pgexecute.py +++ b/pgcli/pgexecute.py @@ -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 |