1
0
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:
Irina Truong 2017-04-29 14:55:58 -07:00 committed by GitHub
commit 84d89250cd
11 changed files with 392 additions and 197 deletions

View File

@ -17,6 +17,7 @@ Core Devs:
Contributors:
-------------
* Brett
* Étienne BERSAC (bersace)
* Daniel Schwarz
* inkn
* Jonathan Slenders

View File

@ -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

View File

@ -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])

View File

@ -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",

View File

@ -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(),)

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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':

View File

@ -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

View File

@ -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))