mirror of
https://github.com/dbcli/pgcli
synced 2024-05-31 01:17:54 +00:00
Merge branch 'master' into feature/behave_fix_cleanup
This commit is contained in:
commit
84d89250cd
1
AUTHORS
1
AUTHORS
|
@ -17,6 +17,7 @@ Core Devs:
|
|||
Contributors:
|
||||
-------------
|
||||
* Brett
|
||||
* Étienne BERSAC (bersace)
|
||||
* Daniel Schwarz
|
||||
* inkn
|
||||
* Jonathan Slenders
|
||||
|
|
|
@ -7,6 +7,7 @@ Features:
|
|||
* Casing for column headers (Thanks: `Joakim Koljonen`_)
|
||||
* Allow configurable character to be used for multi-line query continuations. (Thanks: `Owen Stephens`_)
|
||||
* Completions after ORDER BY and DISTINCT now take account of table aliases. (Thanks: `Owen Stephens`_)
|
||||
* Narrow keyword candidates based on previous keyword. (Thanks: `Étienne Bersac`_)
|
||||
|
||||
Bug fixes:
|
||||
----------
|
||||
|
@ -20,6 +21,7 @@ Internal changes:
|
|||
* Behave quit pgcli nicely (Thanks: `Dick Marinus`_).
|
||||
* Behave test source command (Thanks: `Dick Marinus`_).
|
||||
* Behave fix clean up. (Thanks: `Dick Marinus`_).
|
||||
* Test using behave the tee command (Thanks: `Dick Marinus`_).
|
||||
|
||||
1.5.1
|
||||
=====
|
||||
|
@ -678,3 +680,4 @@ Improvements:
|
|||
.. _`Owen Stephens`: https://github.com/owst
|
||||
.. _`Russell Davies`: https://github.com/russelldavies
|
||||
.. _`Dick Marinus`: https://github.com/meeuw
|
||||
.. _`Étienne Bersac`: https://github.com/bersace
|
||||
|
|
|
@ -8,8 +8,8 @@ with open(literal_file) as f:
|
|||
literals = json.load(f)
|
||||
|
||||
|
||||
def get_literals(literal_type):
|
||||
"""Where `literal_type` is one of 'keywords', 'functions', 'datatypes',
|
||||
returns a tuple of literal values of that type"""
|
||||
def get_literals(literal_type, type_=tuple):
|
||||
# Where `literal_type` is one of 'keywords', 'functions', 'datatypes',
|
||||
# returns a tuple of literal values of that type.
|
||||
|
||||
return tuple(literals[literal_type])
|
||||
return type_(literals[literal_type])
|
||||
|
|
|
@ -1,153 +1,261 @@
|
|||
{
|
||||
"keywords": [
|
||||
"ACCESS",
|
||||
"ADD",
|
||||
"ALL",
|
||||
"ALTER TABLE",
|
||||
"AND",
|
||||
"ANY",
|
||||
"AS",
|
||||
"ASC",
|
||||
"AUDIT",
|
||||
"BETWEEN",
|
||||
"BY",
|
||||
"CASE",
|
||||
"CHAR",
|
||||
"CHECK",
|
||||
"CLUSTER",
|
||||
"COLUMN",
|
||||
"COMMENT",
|
||||
"COMPRESS",
|
||||
"CONCURRENTLY",
|
||||
"CONNECT",
|
||||
"COPY",
|
||||
"CREATE",
|
||||
"CURRENT",
|
||||
"DATABASE",
|
||||
"DATE",
|
||||
"DECIMAL",
|
||||
"DEFAULT",
|
||||
"DELETE FROM",
|
||||
"DELIMITER",
|
||||
"DESC",
|
||||
"DESCRIBE",
|
||||
"DISTINCT",
|
||||
"DROP",
|
||||
"EXPLAIN",
|
||||
"ELSE",
|
||||
"ENCODING",
|
||||
"ESCAPE",
|
||||
"EXCLUSIVE",
|
||||
"EXISTS",
|
||||
"EXTENSION",
|
||||
"FILE",
|
||||
"FLOAT",
|
||||
"FOR",
|
||||
"FORMAT",
|
||||
"FORCE_QUOTE",
|
||||
"FORCE_NOT_NULL",
|
||||
"FREEZE",
|
||||
"FROM",
|
||||
"FULL",
|
||||
"FUNCTION",
|
||||
"GRANT",
|
||||
"GROUP BY",
|
||||
"HAVING",
|
||||
"HEADER",
|
||||
"IDENTIFIED",
|
||||
"IMMEDIATE",
|
||||
"IN",
|
||||
"INCREMENT",
|
||||
"INDEX",
|
||||
"INITIAL",
|
||||
"INSERT INTO",
|
||||
"INTEGER",
|
||||
"INTERSECT",
|
||||
"INTERVAL",
|
||||
"INTO",
|
||||
"IS",
|
||||
"JOIN",
|
||||
"LANGUAGE",
|
||||
"LEFT",
|
||||
"LEVEL",
|
||||
"LIKE",
|
||||
"LIMIT",
|
||||
"LOCK",
|
||||
"LONG",
|
||||
"MATERIALIZED VIEW",
|
||||
"MAXEXTENTS",
|
||||
"MINUS",
|
||||
"MLSLABEL",
|
||||
"MODE",
|
||||
"MODIFY",
|
||||
"NOT",
|
||||
"NOAUDIT",
|
||||
"NOTICE",
|
||||
"NOCOMPRESS",
|
||||
"NOWAIT",
|
||||
"NULL",
|
||||
"NUMBER",
|
||||
"OIDS",
|
||||
"OF",
|
||||
"OFFLINE",
|
||||
"ON",
|
||||
"ONLINE",
|
||||
"OPTION",
|
||||
"OR",
|
||||
"ORDER BY",
|
||||
"OUTER",
|
||||
"OWNER",
|
||||
"PCTFREE",
|
||||
"PRIMARY",
|
||||
"PRIOR",
|
||||
"PRIVILEGES",
|
||||
"QUOTE",
|
||||
"RAISE",
|
||||
"RENAME",
|
||||
"REPLACE",
|
||||
"RAW",
|
||||
"REFRESH MATERIALIZED VIEW",
|
||||
"RESOURCE",
|
||||
"RETURNS",
|
||||
"REVOKE",
|
||||
"RIGHT",
|
||||
"ROW",
|
||||
"ROWID",
|
||||
"ROWNUM",
|
||||
"ROWS",
|
||||
"SELECT",
|
||||
"SESSION",
|
||||
"SET",
|
||||
"SHARE",
|
||||
"SIZE",
|
||||
"SMALLINT",
|
||||
"START",
|
||||
"SUCCESSFUL",
|
||||
"SYNONYM",
|
||||
"SYSDATE",
|
||||
"TABLE",
|
||||
"TEMPLATE",
|
||||
"THEN",
|
||||
"TO",
|
||||
"TRIGGER",
|
||||
"TRUNCATE",
|
||||
"UID",
|
||||
"UNION",
|
||||
"UNIQUE",
|
||||
"UPDATE",
|
||||
"USE",
|
||||
"USER",
|
||||
"USING",
|
||||
"VALIDATE",
|
||||
"VALUES",
|
||||
"VARCHAR",
|
||||
"VARCHAR2",
|
||||
"VIEW",
|
||||
"WHEN",
|
||||
"WHENEVER",
|
||||
"WHERE",
|
||||
"WITH"
|
||||
],
|
||||
"keywords": {
|
||||
"ACCESS": [],
|
||||
"ADD": [],
|
||||
"ALL": [],
|
||||
"ALTER": [
|
||||
"AGGREGATE",
|
||||
"COLLATION",
|
||||
"COLUMN",
|
||||
"CONVERSION",
|
||||
"DATABASE",
|
||||
"DEFAULT",
|
||||
"DOMAIN",
|
||||
"EVENT TRIGGER",
|
||||
"EXTENSION",
|
||||
"FOREIGN",
|
||||
"FUNCTION",
|
||||
"GROUP",
|
||||
"INDEX",
|
||||
"LANGUAGE",
|
||||
"LARGE OBJECT",
|
||||
"MATERIALIZED VIEW",
|
||||
"OPERATOR",
|
||||
"POLICY",
|
||||
"ROLE",
|
||||
"RULE",
|
||||
"SCHEMA",
|
||||
"SEQUENCE",
|
||||
"SERVER",
|
||||
"SYSTEM",
|
||||
"TABLE",
|
||||
"TABLESPACE",
|
||||
"TEXT SEARCH",
|
||||
"TRIGGER",
|
||||
"TYPE",
|
||||
"USER",
|
||||
"VIEW"
|
||||
],
|
||||
"AND": [],
|
||||
"ANY": [],
|
||||
"AS": [],
|
||||
"ASC": [],
|
||||
"AUDIT": [],
|
||||
"BEGIN": [],
|
||||
"BETWEEN": [],
|
||||
"BY": [],
|
||||
"CASE": [],
|
||||
"CHAR": [],
|
||||
"CHECK": [],
|
||||
"CLUSTER": [],
|
||||
"COLUMN": [],
|
||||
"COMMENT": [],
|
||||
"COMPRESS": [],
|
||||
"CONCURRENTLY": [],
|
||||
"CONNECT": [],
|
||||
"COPY": [],
|
||||
"CREATE": [
|
||||
"ACCESS METHOD",
|
||||
"AGGREGATE",
|
||||
"CAST",
|
||||
"COLLATION",
|
||||
"CONVERSION",
|
||||
"DATABASE",
|
||||
"DOMAIN",
|
||||
"EVENT TRIGGER",
|
||||
"EXTENSION",
|
||||
"FOREIGN DATA WRAPPER",
|
||||
"FOREIGN EXTENSION",
|
||||
"FUNCTION",
|
||||
"GLOBAL",
|
||||
"GROUP",
|
||||
"IF NOT EXISTS",
|
||||
"INDEX",
|
||||
"LANGUAGE",
|
||||
"LOCAL",
|
||||
"MATERIALIZED VIEW",
|
||||
"OPERATOR",
|
||||
"OR REPLACE",
|
||||
"POLICY",
|
||||
"ROLE",
|
||||
"RULE",
|
||||
"SCHEMA",
|
||||
"SEQUENCE",
|
||||
"SERVER",
|
||||
"TABLE",
|
||||
"TABLESPACE",
|
||||
"TEMPORARY",
|
||||
"TEXT SEARCH",
|
||||
"TRIGGER",
|
||||
"TYPE",
|
||||
"UNIQUE",
|
||||
"UNLOGGED",
|
||||
"USER",
|
||||
"USER MAPPING",
|
||||
"VIEW"
|
||||
],
|
||||
"CURRENT": [],
|
||||
"DATABASE": [],
|
||||
"DATE": [],
|
||||
"DECIMAL": [],
|
||||
"DEFAULT": [],
|
||||
"DELETE FROM": [],
|
||||
"DELIMITER": [],
|
||||
"DESC": [],
|
||||
"DESCRIBE": [],
|
||||
"DISTINCT": [],
|
||||
"DROP": [
|
||||
"ACCESS METHOD",
|
||||
"AGGREGATE",
|
||||
"CAST",
|
||||
"COLLATION",
|
||||
"CONVERSION",
|
||||
"DATABASE",
|
||||
"DOMAIN",
|
||||
"EVENT TRIGGER",
|
||||
"EXTENSION",
|
||||
"FOREIGN DATA WRAPPER",
|
||||
"FOREIGN TABLE",
|
||||
"FUNCTION",
|
||||
"GROUP",
|
||||
"INDEX",
|
||||
"LANGUAGE",
|
||||
"MATERIALIZED VIEW",
|
||||
"OPERATOR",
|
||||
"OWNED",
|
||||
"POLICY",
|
||||
"ROLE",
|
||||
"RULE",
|
||||
"SCHEMA",
|
||||
"SEQUENCE",
|
||||
"SERVER",
|
||||
"TABLE",
|
||||
"TABLESPACE",
|
||||
"TEXT SEARCH",
|
||||
"TRANSFORM",
|
||||
"TRIGGER",
|
||||
"TYPE",
|
||||
"USER",
|
||||
"USER MAPPING",
|
||||
"VIEW"
|
||||
],
|
||||
"EXPLAIN": [],
|
||||
"ELSE": [],
|
||||
"ENCODING": [],
|
||||
"ESCAPE": [],
|
||||
"EXCLUSIVE": [],
|
||||
"EXISTS": [],
|
||||
"EXTENSION": [],
|
||||
"FILE": [],
|
||||
"FLOAT": [],
|
||||
"FOR": [],
|
||||
"FORMAT": [],
|
||||
"FORCE_QUOTE": [],
|
||||
"FORCE_NOT_NULL": [],
|
||||
"FREEZE": [],
|
||||
"FROM": [],
|
||||
"FULL": [],
|
||||
"FUNCTION": [],
|
||||
"GRANT": [],
|
||||
"GROUP BY": [],
|
||||
"HAVING": [],
|
||||
"HEADER": [],
|
||||
"IDENTIFIED": [],
|
||||
"IMMEDIATE": [],
|
||||
"IN": [],
|
||||
"INCREMENT": [],
|
||||
"INDEX": [],
|
||||
"INITIAL": [],
|
||||
"INSERT INTO": [],
|
||||
"INTEGER": [],
|
||||
"INTERSECT": [],
|
||||
"INTERVAL": [],
|
||||
"INTO": [],
|
||||
"IS": [],
|
||||
"JOIN": [],
|
||||
"LANGUAGE": [],
|
||||
"LEFT": [],
|
||||
"LEVEL": [],
|
||||
"LIKE": [],
|
||||
"LIMIT": [],
|
||||
"LOCK": [],
|
||||
"LONG": [],
|
||||
"MATERIALIZED VIEW": [],
|
||||
"MAXEXTENTS": [],
|
||||
"MINUS": [],
|
||||
"MLSLABEL": [],
|
||||
"MODE": [],
|
||||
"MODIFY": [],
|
||||
"NOT": [],
|
||||
"NOAUDIT": [],
|
||||
"NOTICE": [],
|
||||
"NOCOMPRESS": [],
|
||||
"NOWAIT": [],
|
||||
"NULL": [],
|
||||
"NUMBER": [],
|
||||
"OIDS": [],
|
||||
"OF": [],
|
||||
"OFFLINE": [],
|
||||
"ON": [],
|
||||
"ONLINE": [],
|
||||
"OPTION": [],
|
||||
"OR": [],
|
||||
"ORDER BY": [],
|
||||
"OUTER": [],
|
||||
"OWNER": [],
|
||||
"PCTFREE": [],
|
||||
"PRIMARY": [],
|
||||
"PRIOR": [],
|
||||
"PRIVILEGES": [],
|
||||
"QUOTE": [],
|
||||
"RAISE": [],
|
||||
"RENAME": [],
|
||||
"REPLACE": [],
|
||||
"RESET": ["ALL"],
|
||||
"RAW": [],
|
||||
"REFRESH MATERIALIZED VIEW": [],
|
||||
"RESOURCE": [],
|
||||
"RETURNS": [],
|
||||
"REVOKE": [],
|
||||
"RIGHT": [],
|
||||
"ROW": [],
|
||||
"ROWID": [],
|
||||
"ROWNUM": [],
|
||||
"ROWS": [],
|
||||
"SELECT": [],
|
||||
"SESSION": [],
|
||||
"SET": [],
|
||||
"SHARE": [],
|
||||
"SHOW": [],
|
||||
"SIZE": [],
|
||||
"SMALLINT": [],
|
||||
"START": [],
|
||||
"SUCCESSFUL": [],
|
||||
"SYNONYM": [],
|
||||
"SYSDATE": [],
|
||||
"TABLE": [],
|
||||
"TEMPLATE": [],
|
||||
"THEN": [],
|
||||
"TO": [],
|
||||
"TRIGGER": [],
|
||||
"TRUNCATE": [],
|
||||
"UID": [],
|
||||
"UNION": [],
|
||||
"UNIQUE": [],
|
||||
"UPDATE": [],
|
||||
"USE": [],
|
||||
"USER": [],
|
||||
"USING": [],
|
||||
"VALIDATE": [],
|
||||
"VALUES": [],
|
||||
"VARCHAR": [],
|
||||
"VARCHAR2": [],
|
||||
"VIEW": [],
|
||||
"WHEN": [],
|
||||
"WHENEVER": [],
|
||||
"WHERE": [],
|
||||
"WITH": []
|
||||
},
|
||||
"functions": [
|
||||
"AVG",
|
||||
"COUNT",
|
||||
|
|
|
@ -46,7 +46,8 @@ Column = namedtuple(
|
|||
)
|
||||
Column.__new__.__defaults__ = (None, None, tuple(), False)
|
||||
|
||||
Keyword = namedtuple('Keyword', [])
|
||||
Keyword = namedtuple('Keyword', ['last_token'])
|
||||
Keyword.__new__.__defaults__ = (None,)
|
||||
NamedQuery = namedtuple('NamedQuery', [])
|
||||
Datatype = namedtuple('Datatype', ['schema'])
|
||||
Alias = namedtuple('Alias', ['aliases'])
|
||||
|
@ -394,7 +395,7 @@ def suggest_based_on_last_token(token, stmt):
|
|||
return (Column(table_refs=tables, local_tables=stmt.local_tables,
|
||||
qualifiable=True),
|
||||
Function(schema=None),
|
||||
Keyword(),)
|
||||
Keyword(token_v.upper()),)
|
||||
elif token_v == 'as':
|
||||
# Don't suggest anything for aliases
|
||||
return ()
|
||||
|
@ -491,8 +492,8 @@ def suggest_based_on_last_token(token, stmt):
|
|||
if not schema:
|
||||
suggestions.append(Schema())
|
||||
return tuple(suggestions)
|
||||
elif token_v == 'alter':
|
||||
return (Keyword(),)
|
||||
elif token_v in {'alter', 'create', 'drop'}:
|
||||
return (Keyword(token_v.upper()),)
|
||||
elif token.is_keyword:
|
||||
# token is a keyword we haven't implemented any special handling for
|
||||
# go backwards in the query until we find one we do recognize
|
||||
|
@ -500,7 +501,7 @@ def suggest_based_on_last_token(token, stmt):
|
|||
if prev_keyword:
|
||||
return suggest_based_on_last_token(prev_keyword, stmt)
|
||||
else:
|
||||
return (Keyword(),)
|
||||
return (Keyword(token_v.upper()),)
|
||||
else:
|
||||
return (Keyword(),)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import print_function, unicode_literals
|
||||
import logging
|
||||
import re
|
||||
from itertools import count, repeat
|
||||
from itertools import count, repeat, chain
|
||||
import operator
|
||||
from collections import namedtuple, defaultdict
|
||||
from pgspecial.namedqueries import NamedQueries
|
||||
|
@ -51,7 +51,10 @@ def generate_alias(tbl):
|
|||
[l for l, prev in zip(tbl, '_' + tbl) if prev == '_' and l != '_'])
|
||||
|
||||
class PGCompleter(Completer):
|
||||
keywords = get_literals('keywords')
|
||||
# keywords_tree: A dict mapping keywords to well known following keywords.
|
||||
# e.g. 'CREATE': ['TABLE', 'USER', ...],
|
||||
keywords_tree = get_literals('keywords', type_=dict)
|
||||
keywords = tuple(set(chain(keywords_tree.keys(), *keywords_tree.values())))
|
||||
functions = get_literals('functions')
|
||||
datatypes = get_literals('datatypes')
|
||||
|
||||
|
@ -649,7 +652,14 @@ class PGCompleter(Completer):
|
|||
return self.find_matches(word_before_cursor, self.databases,
|
||||
meta='database')
|
||||
|
||||
def get_keyword_matches(self, _, word_before_cursor):
|
||||
def get_keyword_matches(self, suggestion, word_before_cursor):
|
||||
keywords = self.keywords_tree.keys()
|
||||
# Get well known following keywords for the last token. If any, narrow
|
||||
# candidates to this list.
|
||||
next_keywords = self.keywords_tree.get(suggestion.last_token, [])
|
||||
if next_keywords:
|
||||
keywords = next_keywords
|
||||
|
||||
casing = self.keyword_casing
|
||||
if casing == 'auto':
|
||||
if word_before_cursor and word_before_cursor[-1].islower():
|
||||
|
@ -658,9 +668,9 @@ class PGCompleter(Completer):
|
|||
casing = 'upper'
|
||||
|
||||
if casing == 'upper':
|
||||
keywords = [k.upper() for k in self.keywords]
|
||||
keywords = [k.upper() for k in keywords]
|
||||
else:
|
||||
keywords = [k.lower() for k in self.keywords]
|
||||
keywords = [k.lower() for k in keywords]
|
||||
|
||||
return self.find_matches(word_before_cursor, keywords,
|
||||
mode='strict', meta='keyword')
|
||||
|
|
|
@ -8,3 +8,14 @@ Feature: I/O commands
|
|||
and we exit the editor
|
||||
then we see dbcli prompt
|
||||
and we see the sql in prompt
|
||||
|
||||
Scenario: tee output from query
|
||||
When we run dbcli
|
||||
and we wait for prompt
|
||||
and we tee output
|
||||
and we wait for prompt
|
||||
and we query "select 123456"
|
||||
and we wait for prompt
|
||||
and we notee output
|
||||
and we wait for prompt
|
||||
then we see 123456 in tee output
|
||||
|
|
|
@ -43,5 +43,43 @@ def step_edit_done_sql(context):
|
|||
# Cleanup the command line.
|
||||
context.cli.sendcontrol('c')
|
||||
# Cleanup the edited file.
|
||||
if os.path.join(*context.editor_file_name) and os.path.exists(os.path.join(*context.editor_file_name)):
|
||||
os.remove(os.path.join(*context.editor_file_name))
|
||||
full_tmp_file_name = os.path.join(*context.editor_file_name)
|
||||
if full_tmp_file_name and os.path.exists(full_tmp_file_name):
|
||||
os.remove(full_tmp_file_name)
|
||||
|
||||
|
||||
@when(u'we tee output')
|
||||
def step_tee_ouptut(context):
|
||||
context.tee_file_name = [
|
||||
'..',
|
||||
'tee_file_{0}.sql'.format(context.conf['vi'])
|
||||
]
|
||||
if os.path.exists(os.path.join(*context.tee_file_name)):
|
||||
os.remove(os.path.join(*context.tee_file_name))
|
||||
context.cli.sendline('\o {0}'.format(context.tee_file_name[1]))
|
||||
wrappers.expect_exact(
|
||||
context, context.conf['pager_boundary'] + '\r\n', timeout=5)
|
||||
wrappers.expect_exact(context, "Writing to file", timeout=5)
|
||||
wrappers.expect_exact(
|
||||
context, context.conf['pager_boundary'] + '\r\n', timeout=5)
|
||||
wrappers.expect_exact(context, "Time", timeout=5)
|
||||
|
||||
|
||||
@when(u'we query "select 123456"')
|
||||
def step_query_select_123456(context):
|
||||
context.cli.sendline('select 123456')
|
||||
|
||||
|
||||
@when(u'we notee output')
|
||||
def step_notee_output(context):
|
||||
context.cli.sendline('notee')
|
||||
wrappers.expect_exact(context, "Time", timeout=5)
|
||||
|
||||
|
||||
@then(u'we see 123456 in tee output')
|
||||
def step_see_123456_in_ouput(context):
|
||||
with open(os.path.join(*context.tee_file_name)) as f:
|
||||
assert '123456' in f.read()
|
||||
if os.path.exists(os.path.join(*context.tee_file_name)):
|
||||
os.remove(os.path.join(*context.tee_file_name))
|
||||
context.atprompt = True
|
||||
|
|
|
@ -66,7 +66,7 @@ class MetaData(object):
|
|||
return [datatype(dt, pos) for dt in self.completer.datatypes]
|
||||
|
||||
def keywords(self, pos=0):
|
||||
return [keyword(kw, pos) for kw in self.completer.keywords]
|
||||
return [keyword(kw, pos) for kw in self.completer.keywords_tree.keys()]
|
||||
|
||||
def columns(self, tbl, parent='public', typ='tables', pos=0):
|
||||
if typ == 'functions':
|
||||
|
|
|
@ -56,3 +56,18 @@ def test_paths_completion(completer, complete_event):
|
|||
complete_event,
|
||||
smart_completion=True))
|
||||
assert result > set([Completion(text="setup.py", start_position=0)])
|
||||
|
||||
|
||||
def test_alter_well_known_keywords_completion(completer, complete_event):
|
||||
text = 'ALTER '
|
||||
position = len(text)
|
||||
result = set(completer.get_completions(
|
||||
Document(text=text, cursor_position=position),
|
||||
complete_event,
|
||||
smart_completion=True))
|
||||
assert result > set([
|
||||
Completion(text="DATABASE", display_meta='keyword'),
|
||||
Completion(text="TABLE", display_meta='keyword'),
|
||||
Completion(text="SYSTEM", display_meta='keyword'),
|
||||
])
|
||||
assert Completion(text="CREATE", display_meta="keyword") not in result
|
||||
|
|
|
@ -4,23 +4,26 @@ from pgcli.packages.sqlcompletion import (
|
|||
from pgcli.packages.parseutils.tables import TableReference
|
||||
import pytest
|
||||
|
||||
def cols_etc(table, schema=None, alias=None, is_function=False, parent=None):
|
||||
|
||||
def cols_etc(table, schema=None, alias=None, is_function=False, parent=None,
|
||||
last_keyword=None):
|
||||
"""Returns the expected select-clause suggestions for a single-table
|
||||
select."""
|
||||
return set([
|
||||
Column(table_refs=(TableReference(schema, table, alias, is_function),),
|
||||
qualifiable=True),
|
||||
Function(schema=parent),
|
||||
Keyword()])
|
||||
Keyword(last_keyword)])
|
||||
|
||||
|
||||
def test_select_suggests_cols_with_visible_table_scope():
|
||||
suggestions = suggest_type('SELECT FROM tabl', 'SELECT ')
|
||||
assert set(suggestions) == cols_etc('tabl')
|
||||
assert set(suggestions) == cols_etc('tabl', last_keyword='SELECT')
|
||||
|
||||
|
||||
def test_select_suggests_cols_with_qualified_table_scope():
|
||||
suggestions = suggest_type('SELECT FROM sch.tabl', 'SELECT ')
|
||||
assert set(suggestions) == cols_etc('tabl', 'sch')
|
||||
assert set(suggestions) == cols_etc('tabl', 'sch', last_keyword='SELECT')
|
||||
|
||||
|
||||
def test_cte_does_not_crash():
|
||||
|
@ -33,8 +36,9 @@ def test_cte_does_not_crash():
|
|||
'SELECT * FROM "tabl" WHERE ',
|
||||
])
|
||||
def test_where_suggests_columns_functions_quoted_table(expression):
|
||||
expected = cols_etc('tabl', alias='"tabl"', last_keyword='WHERE')
|
||||
suggestions = suggest_type(expression, expression)
|
||||
assert set(suggestions) == cols_etc('tabl', alias='"tabl"')
|
||||
assert expected == set(suggestions)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('expression', [
|
||||
|
@ -53,7 +57,7 @@ def test_where_suggests_columns_functions_quoted_table(expression):
|
|||
])
|
||||
def test_where_suggests_columns_functions(expression):
|
||||
suggestions = suggest_type(expression, expression)
|
||||
assert set(suggestions) == cols_etc('tabl')
|
||||
assert set(suggestions) == cols_etc('tabl', last_keyword='WHERE')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('expression', [
|
||||
|
@ -62,7 +66,7 @@ def test_where_suggests_columns_functions(expression):
|
|||
])
|
||||
def test_where_in_suggests_columns(expression):
|
||||
suggestions = suggest_type(expression, expression)
|
||||
assert set(suggestions) == cols_etc('tabl')
|
||||
assert set(suggestions) == cols_etc('tabl', last_keyword='WHERE')
|
||||
|
||||
@pytest.mark.parametrize('expression', [
|
||||
'SELECT 1 AS ',
|
||||
|
@ -76,7 +80,7 @@ def test_after_as(expression):
|
|||
def test_where_equals_any_suggests_columns_or_keywords():
|
||||
text = 'SELECT * FROM tabl WHERE foo = ANY('
|
||||
suggestions = suggest_type(text, text)
|
||||
assert set(suggestions) == cols_etc('tabl')
|
||||
assert set(suggestions) == cols_etc('tabl', last_keyword='WHERE')
|
||||
|
||||
|
||||
def test_lparen_suggests_cols():
|
||||
|
@ -88,9 +92,9 @@ def test_lparen_suggests_cols():
|
|||
def test_select_suggests_cols_and_funcs():
|
||||
suggestions = suggest_type('SELECT ', 'SELECT ')
|
||||
assert set(suggestions) == set([
|
||||
Column(table_refs=(), qualifiable=True),
|
||||
Function(schema=None),
|
||||
Keyword(),
|
||||
Column(table_refs=(), qualifiable=True),
|
||||
Function(schema=None),
|
||||
Keyword('SELECT'),
|
||||
])
|
||||
|
||||
|
||||
|
@ -217,21 +221,24 @@ def test_distinct_suggests_cols(text):
|
|||
assert set(suggestions) == set([
|
||||
Column(table_refs=(), local_tables=(), qualifiable=True),
|
||||
Function(schema=None),
|
||||
Keyword()
|
||||
Keyword('DISTINCT')
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('text, text_before', [
|
||||
@pytest.mark.parametrize('text, text_before, last_keyword', [
|
||||
(
|
||||
'SELECT DISTINCT FROM tbl x JOIN tbl1 y',
|
||||
'SELECT DISTINCT'
|
||||
'SELECT DISTINCT',
|
||||
'SELECT',
|
||||
),
|
||||
(
|
||||
'SELECT * FROM tbl x JOIN tbl1 y ORDER BY ',
|
||||
'SELECT * FROM tbl x JOIN tbl1 y ORDER BY '
|
||||
'SELECT * FROM tbl x JOIN tbl1 y ORDER BY ',
|
||||
'BY',
|
||||
)
|
||||
])
|
||||
def test_distinct_and_order_by_suggestions_with_aliases(text, text_before):
|
||||
def test_distinct_and_order_by_suggestions_with_aliases(text, text_before,
|
||||
last_keyword):
|
||||
suggestions = suggest_type(text, text_before)
|
||||
assert set(suggestions) == set([
|
||||
Column(
|
||||
|
@ -243,7 +250,7 @@ def test_distinct_and_order_by_suggestions_with_aliases(text, text_before):
|
|||
qualifiable=True
|
||||
),
|
||||
Function(schema=None),
|
||||
Keyword()
|
||||
Keyword(last_keyword)
|
||||
])
|
||||
|
||||
|
||||
|
@ -275,7 +282,7 @@ def test_col_comma_suggests_cols():
|
|||
assert set(suggestions) == set([
|
||||
Column(table_refs=((None, 'tbl', None, False),), qualifiable=True),
|
||||
Function(schema=None),
|
||||
Keyword(),
|
||||
Keyword('SELECT'),
|
||||
])
|
||||
|
||||
|
||||
|
@ -318,7 +325,7 @@ def test_insert_into_lparen_comma_suggests_cols():
|
|||
def test_partially_typed_col_name_suggests_col_names():
|
||||
suggestions = suggest_type('SELECT * FROM tabl WHERE col_n',
|
||||
'SELECT * FROM tabl WHERE col_n')
|
||||
assert set(suggestions) == cols_etc('tabl')
|
||||
assert set(suggestions) == cols_etc('tabl', last_keyword='WHERE')
|
||||
|
||||
|
||||
def test_dot_suggests_cols_of_a_table_or_schema_qualified_table():
|
||||
|
@ -435,7 +442,7 @@ def test_sub_select_col_name_completion():
|
|||
assert set(suggestions) == set([
|
||||
Column(table_refs=((None, 'abc', None, False),), qualifiable=True),
|
||||
Function(schema=None),
|
||||
Keyword(),
|
||||
Keyword('SELECT'),
|
||||
])
|
||||
|
||||
|
||||
|
@ -599,7 +606,7 @@ def test_2_statements_2nd_current():
|
|||
assert set(suggestions) == set([
|
||||
Column(table_refs=((None, 'b', None, False),), qualifiable=True),
|
||||
Function(schema=None),
|
||||
Keyword()
|
||||
Keyword('SELECT')
|
||||
])
|
||||
|
||||
# Should work even if first statement is invalid
|
||||
|
@ -621,7 +628,7 @@ def test_2_statements_1st_current():
|
|||
|
||||
suggestions = suggest_type('select from a; select * from b',
|
||||
'select ')
|
||||
assert set(suggestions) == cols_etc('a')
|
||||
assert set(suggestions) == cols_etc('a', last_keyword='SELECT')
|
||||
|
||||
|
||||
def test_3_statements_2nd_current():
|
||||
|
@ -634,7 +641,7 @@ def test_3_statements_2nd_current():
|
|||
|
||||
suggestions = suggest_type('select * from a; select from b; select * from c',
|
||||
'select * from a; select ')
|
||||
assert set(suggestions) == cols_etc('b')
|
||||
assert set(suggestions) == cols_etc('b', last_keyword='SELECT')
|
||||
|
||||
@pytest.mark.parametrize('text', [
|
||||
'''
|
||||
|
@ -685,7 +692,7 @@ def test_statements_in_function_body(text):
|
|||
assert set(suggestions) == set([
|
||||
Column(table_refs=((None, 'foo', None, False),), qualifiable=True),
|
||||
Function(schema=None),
|
||||
Keyword()
|
||||
Keyword('SELECT'),
|
||||
])
|
||||
|
||||
functions = [
|
||||
|
@ -709,12 +716,13 @@ SELECT 1 FROM foo;
|
|||
@pytest.mark.parametrize('text', functions)
|
||||
def test_statements_with_cursor_after_function_body(text):
|
||||
suggestions = suggest_type(text, text[:text.find('; ') + 1])
|
||||
assert set(suggestions) == set([Keyword()])
|
||||
assert set(suggestions) == set([Keyword(), Special()])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('text', functions)
|
||||
def test_statements_with_cursor_before_function_body(text):
|
||||
suggestions = suggest_type(text, '')
|
||||
assert set(suggestions) == set([Keyword()])
|
||||
assert set(suggestions) == set([Keyword(), Special()])
|
||||
|
||||
def test_create_db_with_template():
|
||||
suggestions = suggest_type('create database foo with template ',
|
||||
|
@ -828,16 +836,16 @@ def test_invalid_sql():
|
|||
def test_suggest_where_keyword(text):
|
||||
# https://github.com/dbcli/mycli/issues/135
|
||||
suggestions = suggest_type(text, text)
|
||||
assert set(suggestions) == cols_etc('foo')
|
||||
assert set(suggestions) == cols_etc('foo', last_keyword='WHERE')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('text, before, expected', [
|
||||
('\\ns abc SELECT ', 'SELECT ', [
|
||||
Column(table_refs=(), qualifiable=True),
|
||||
Function(schema=None),
|
||||
Keyword()
|
||||
Keyword('SELECT')
|
||||
]),
|
||||
('\\ns abc SELECT foo ', 'SELECT foo ',(Keyword(),)),
|
||||
('\\ns abc SELECT foo ', 'SELECT foo ', (Keyword(),)),
|
||||
('\\ns abc SELECT t1. FROM tabl1 t1', 'SELECT t1.', [
|
||||
Table(schema='t1'),
|
||||
View(schema='t1'),
|
||||
|
@ -853,7 +861,7 @@ def test_named_query_completion(text, before, expected):
|
|||
def test_select_suggests_fields_from_function():
|
||||
suggestions = suggest_type('SELECT FROM func()', 'SELECT ')
|
||||
assert set(suggestions) == cols_etc(
|
||||
'func', is_function=True)
|
||||
'func', is_function=True, last_keyword='SELECT')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('sql', [
|
||||
|
@ -900,4 +908,4 @@ def test_handle_unrecognized_kw_generously():
|
|||
'ALTER TABLE foo ALTER ',
|
||||
])
|
||||
def test_keyword_after_alter(sql):
|
||||
assert Keyword() in set(suggest_type(sql, sql))
|
||||
assert Keyword('ALTER') in set(suggest_type(sql, sql))
|
||||
|
|
Loading…
Reference in New Issue
Block a user