mirror of https://github.com/dbcli/pgcli
Autorefresh for \c, update readme with thanks.
This commit is contained in:
parent
a88192bc0c
commit
60cbbdcb28
17
README.rst
17
README.rst
|
@ -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
13
TODO
|
@ -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.
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue