Skip to content
This repository was archived by the owner on Jan 3, 2021. It is now read-only.

Commit 09c5f28

Browse files
author
Amjith Ramanujam
committed
First attempt at dot completion and alias detection.
1 parent 389777e commit 09c5f28

3 files changed

Lines changed: 56 additions & 34 deletions

File tree

pgcli/packages/parseutils.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# This matches only alphanumerics and underscores.
88
'alphanum_underscore': re.compile(r'(\w+)$'),
99
# This matches everything except spaces, parens and comma.
10-
'most_punctuations': re.compile(r'([^(),\s]+)$'),
10+
'most_punctuations': re.compile(r'([^\.(),\s]+)$'),
1111
# This matches everything except a space.
1212
'all_punctuations': re.compile('([^\s]+)$'),
1313
}
@@ -93,18 +93,23 @@ def extract_table_identifiers(token_stream):
9393
for item in token_stream:
9494
if isinstance(item, IdentifierList):
9595
for identifier in item.get_identifiers():
96-
yield identifier.get_real_name()
96+
real_name = identifier.get_real_name()
97+
yield (real_name, identifier.get_alias() or real_name)
9798
elif isinstance(item, Identifier):
98-
yield item.get_real_name()
99+
real_name = item.get_real_name()
100+
yield (real_name, item.get_alias() or real_name)
99101
elif isinstance(item, Function):
100-
yield item.get_name()
102+
yield (item.get_name(), item.get_name())
101103
# It's a bug to check for Keyword here, but in the example
102104
# above some tables names are identified as keywords...
103105
elif item.ttype is Keyword:
104-
yield item.value
106+
yield (item.value, item.value)
105107

106-
def extract_tables(sql):
108+
def extract_tables(sql, include_alias=False):
107109
if not sql:
108110
return []
109111
stream = extract_from_part(sqlparse.parse(sql)[0])
110-
return list(extract_table_identifiers(stream))
112+
if include_alias:
113+
return dict((alias, t) for t, alias in extract_table_identifiers(stream))
114+
else:
115+
return [x[0] for x in extract_table_identifiers(stream)]

pgcli/packages/sqlcompletion.py

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ def suggest_type(full_text, text_before_cursor):
1111
A scope for a column category will be a list of tables.
1212
"""
1313

14-
tables = extract_tables(full_text)
1514

1615
word_before_cursor = last_word(text_before_cursor,
1716
include='all_punctuations')
@@ -22,45 +21,53 @@ def suggest_type(full_text, text_before_cursor):
2221
# partially typed string which renders the smart completion useless because
2322
# it will always return the list of keywords as completion.
2423
if word_before_cursor:
25-
parsed = sqlparse.parse(
26-
text_before_cursor[:-len(word_before_cursor)])
24+
if word_before_cursor[-1] in ('.'):
25+
parsed = sqlparse.parse(text_before_cursor)
26+
else:
27+
parsed = sqlparse.parse(
28+
text_before_cursor[:-len(word_before_cursor)])
2729
else:
2830
parsed = sqlparse.parse(text_before_cursor)
2931

3032
# Need to check if `p` is not empty, since an empty string will result in
3133
# an empty tuple.
3234
p = parsed[0] if parsed else None
33-
n = p and len(p.tokens) or 0
34-
last_token = p and p.token_prev(n) or ''
35-
last_token_v = last_token.value if last_token else ''
35+
last_token = p and p.token_prev(len(p.tokens)) or ''
3636

3737
def is_function_word(word):
38-
return word and len(word) > 1 and word[-1] == '('
38+
return word.endswith('(')
39+
3940
if is_function_word(word_before_cursor):
40-
return ('columns', tables)
41+
return ('columns', extract_tables(full_text))
4142

42-
return (suggest_based_on_last_token(last_token_v, text_before_cursor),
43-
tables)
43+
return suggest_based_on_last_token(last_token, text_before_cursor, full_text)
4444

45+
def suggest_based_on_last_token(token, text_before_cursor, full_text):
46+
if isinstance(token, basestring):
47+
token_v = token
48+
else:
49+
token_v = token.value
4550

46-
def suggest_based_on_last_token(last_token_v, text_before_cursor):
47-
if last_token_v.lower().endswith('('):
48-
return 'columns'
49-
if last_token_v.lower() in ('set', 'by', 'distinct'):
50-
return 'columns'
51-
elif last_token_v.lower() in ('select', 'where', 'having'):
52-
return 'columns-and-functions'
53-
elif last_token_v.lower() in ('from', 'update', 'into', 'describe'):
54-
return 'tables'
55-
elif last_token_v in ('d',): # \d
56-
return 'tables'
57-
elif last_token_v.lower() in ('c', 'use'): # \c
58-
return 'databases'
59-
elif last_token_v.endswith(','):
51+
if token_v.lower().endswith('('):
52+
return 'columns', extract_tables(full_text)
53+
if token_v.lower() in ('set', 'by', 'distinct'):
54+
return 'columns', extract_tables(full_text)
55+
elif token_v.lower() in ('select', 'where', 'having'):
56+
return 'columns-and-functions', extract_tables(full_text)
57+
elif token_v.lower() in ('from', 'update', 'into', 'describe'):
58+
return 'tables', []
59+
elif token_v in ('d',): # \d
60+
return 'tables', []
61+
elif token_v.lower() in ('c', 'use'): # \c
62+
return 'databases', []
63+
elif token_v.endswith(','):
6064
prev_keyword = find_prev_keyword(text_before_cursor)
61-
return suggest_based_on_last_token(prev_keyword, text_before_cursor)
65+
return suggest_based_on_last_token(prev_keyword, text_before_cursor, full_text)
66+
elif token_v.endswith('.'):
67+
tables = extract_tables(full_text, include_alias=True)
68+
return 'columns', [tables.get(token.token_first().value)]
6269
else:
63-
return 'keywords'
70+
return 'keywords', []
6471

6572
def find_prev_keyword(sql):
6673
if not sql.strip():

tests/test_sqlcompletion.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_col_comma_suggests_cols():
3333
def test_table_comma_suggests_tables():
3434
suggestion = suggest_type('SELECT a, b FROM tbl1, ',
3535
'SELECT a, b FROM tbl1, ')
36-
assert suggestion == ('tables', ['tbl1'])
36+
assert suggestion == ('tables', [])
3737

3838
def test_into_suggests_tables():
3939
suggestion = suggest_type('INSERT INTO ',
@@ -44,3 +44,13 @@ def test_partially_typed_col_name_suggests_col_names():
4444
suggestion = suggest_type('SELECT * FROM tabl WHERE col_n',
4545
'SELECT * FROM tabl WHERE col_n')
4646
assert suggestion == ('columns-and-functions', ['tabl'])
47+
48+
def test_dot_suggests_cols_of_a_table():
49+
suggestion = suggest_type('SELECT tabl. FROM tabl',
50+
'SELECT tabl.')
51+
assert suggestion == ('columns', ['tabl'])
52+
53+
def test_dot_suggests_cols_of_an_alias():
54+
suggestion = suggest_type('SELECT t1. FROM tabl1 t1, tabl2 t2',
55+
'SELECT t1.')
56+
assert suggestion == ('columns', ['tabl1'])

0 commit comments

Comments
 (0)