summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pgcli/pgcompleter.py71
-rw-r--r--tests/test_smart_completion_multiple_schemata.py13
-rw-r--r--tests/test_smart_completion_public_schema_only.py63
-rw-r--r--tests/test_sqlcompletion.py9
4 files changed, 129 insertions, 27 deletions
diff --git a/pgcli/pgcompleter.py b/pgcli/pgcompleter.py
index ec81f98e..337cf8f7 100644
--- a/pgcli/pgcompleter.py
+++ b/pgcli/pgcompleter.py
@@ -420,21 +420,34 @@ class PGCompleter(Completer):
schema = self.escape_name(tbl.schema)
relname = self.escape_name(tbl.name)
- # We don't know if schema.relname is a table or view. Since
- # tables and views cannot share the same name, we can check one
- # at a time
- try:
- columns.extend(meta['tables'][schema][relname])
+ if tbl.is_function:
+ # Return column names from a set-returning function
+ try:
+ # Get an array of FunctionMetadata objects
+ functions = meta['functions'][schema][relname]
+ except KeyError:
+ # No such function name
+ continue
- # Table exists, so don't bother checking for a view
- continue
- except KeyError:
- pass
+ for func in functions:
+ # func is a FunctionMetadata object
+ columns.extend(func.fieldnames())
+ else:
+ # We don't know if schema.relname is a table or view. Since
+ # tables and views cannot share the same name, we can check
+ # one at a time
+ try:
+ columns.extend(meta['tables'][schema][relname])
- try:
- columns.extend(meta['views'][schema][relname])
- except KeyError:
- pass
+ # Table exists, so don't bother checking for a view
+ continue
+ except KeyError:
+ pass
+
+ try:
+ columns.extend(meta['views'][schema][relname])
+ except KeyError:
+ pass
else:
# Schema not specified, so traverse the search path looking for
@@ -444,17 +457,27 @@ class PGCompleter(Completer):
for schema in self.search_path:
relname = self.escape_name(tbl.name)
- try:
- columns.extend(meta['tables'][schema][relname])
- break
- except KeyError:
- pass
-
- try:
- columns.extend(meta['views'][schema][relname])
- break
- except KeyError:
- pass
+ if tbl.is_function:
+ try:
+ functions = meta['functions'][schema][relname]
+ except KeyError:
+ continue
+
+ for func in functions:
+ # func is a FunctionMetadata object
+ columns.extend(func.fieldnames())
+ else:
+ try:
+ columns.extend(meta['tables'][schema][relname])
+ break
+ except KeyError:
+ pass
+
+ try:
+ columns.extend(meta['views'][schema][relname])
+ break
+ except KeyError:
+ pass
return columns
diff --git a/tests/test_smart_completion_multiple_schemata.py b/tests/test_smart_completion_multiple_schemata.py
index 6597f6d0..7eb81385 100644
--- a/tests/test_smart_completion_multiple_schemata.py
+++ b/tests/test_smart_completion_multiple_schemata.py
@@ -22,7 +22,9 @@ metadata = {
['func2', '', '', False, False, False]],
'custom': [
['func3', '', '', False, False, False],
- ['set_returning_func', '', '', False, False, True]],
+ ['set_returning_func',
+ 'OUT x INT', 'SETOF INT',
+ False, False, True]],
},
'datatypes': {
'public': ['typ1', 'typ2'],
@@ -294,3 +296,12 @@ def test_schema_qualified_type_name(text, completer, complete_event):
Completion(text='products', display_meta='table'),
Completion(text='shipments', display_meta='table'),
])
+
+
+def test_suggest_columns_from_aliased_set_returning_function(completer, complete_event):
+ sql = 'select f. from custom.set_returning_func() f'
+ pos = len('select f.')
+ result = completer.get_completions(Document(text=sql, cursor_position=pos),
+ complete_event)
+ assert set(result) == set([
+ Completion(text='x', start_position=0, display_meta='column')]) \ No newline at end of file
diff --git a/tests/test_smart_completion_public_schema_only.py b/tests/test_smart_completion_public_schema_only.py
index cbf7d990..16e7a5ab 100644
--- a/tests/test_smart_completion_public_schema_only.py
+++ b/tests/test_smart_completion_public_schema_only.py
@@ -14,7 +14,8 @@ metadata = {
'functions': [
['custom_func1', '', '', False, False, False],
['custom_func2', '', '', False, False, False],
- ['set_returning_func', '', '', False, False, True]],
+ ['set_returning_func', '', 'TABLE (x INT, y INT)',
+ False, False, True]],
'datatypes': ['custom_type1', 'custom_type2'],
}
@@ -246,7 +247,7 @@ def test_suggested_multiple_column_names(completer, complete_event):
Completion(text='last_name', start_position=0, display_meta='column'),
Completion(text='custom_func1', start_position=0, display_meta='function'),
Completion(text='custom_func2', start_position=0, display_meta='function'),
- Completion(text='set_returning_func', start_position=0, display_meta='function')] +
+ Completion(text='set_returning_func', start_position=0, display_meta='function')] +
list(map(lambda f: Completion(f, display_meta='function'), completer.functions)) +
list(map(lambda x: Completion(x, display_meta='keyword'), completer.keywords))
)
@@ -416,3 +417,61 @@ def test_suggest_columns_from_escaped_table_alias(completer, complete_event):
Completion(text='"insert"', start_position=0, display_meta='column'),
Completion(text='"ABC"', start_position=0, display_meta='column'),
])
+
+
+def test_suggest_columns_from_set_returning_function(completer, complete_event):
+ sql = 'select from set_returning_func()'
+ pos = len('select ')
+ result = completer.get_completions(Document(text=sql, cursor_position=pos),
+ complete_event)
+ assert set(result) == set([
+ Completion(text='x', start_position=0, display_meta='column'),
+ Completion(text='y', start_position=0, display_meta='column'),
+ Completion(text='custom_func1', start_position=0, display_meta='function'),
+ Completion(text='custom_func2', start_position=0, display_meta='function'),
+ Completion(text='set_returning_func', start_position=0, display_meta='function')]
+ + list(map(lambda f: Completion(f, display_meta='function'), completer.functions))
+ + list(map(lambda x: Completion(x, display_meta='keyword'), completer.keywords)))
+
+
+def test_suggest_columns_from_aliased_set_returning_function(completer, complete_event):
+ sql = 'select f. from set_returning_func() f'
+ pos = len('select f.')
+ result = completer.get_completions(Document(text=sql, cursor_position=pos),
+ complete_event)
+ assert set(result) == set([
+ Completion(text='x', start_position=0, display_meta='column'),
+ Completion(text='y', start_position=0, display_meta='column')])
+
+
+def test_join_functions_using_suggests_common_columns(completer, complete_event):
+ text = '''SELECT * FROM set_returning_func() f1
+ INNER JOIN set_returning_func() f2 USING ('''
+ pos = len(text)
+ result = set(completer.get_completions(
+ Document(text=text, cursor_position=pos), complete_event))
+ assert set(result) == set([
+ Completion(text='x', start_position=0, display_meta='column'),
+ Completion(text='y', start_position=0, display_meta='column')])
+
+
+def test_join_functions_using_suggests_common_columns(completer, complete_event):
+ text = '''SELECT * FROM set_returning_func() f1
+ INNER JOIN set_returning_func() f2 USING ('''
+ pos = len(text)
+ result = set(completer.get_completions(
+ Document(text=text, cursor_position=pos), complete_event))
+ assert set(result) == set([
+ Completion(text='x', start_position=0, display_meta='column'),
+ Completion(text='y', start_position=0, display_meta='column')])
+
+
+def test_join_functions_on_suggests_columns(completer, complete_event):
+ text = '''SELECT * FROM set_returning_func() f1
+ INNER JOIN set_returning_func() f2 ON f1.'''
+ pos = len(text)
+ result = set(completer.get_completions(
+ Document(text=text, cursor_position=pos), complete_event))
+ assert set(result) == set([
+ Completion(text='x', start_position=0, display_meta='column'),
+ Completion(text='y', start_position=0, display_meta='column')]) \ No newline at end of file
diff --git a/tests/test_sqlcompletion.py b/tests/test_sqlcompletion.py
index 3842cd98..c2585336 100644
--- a/tests/test_sqlcompletion.py
+++ b/tests/test_sqlcompletion.py
@@ -612,3 +612,12 @@ def test_suggest_where_keyword(text):
def test_named_query_completion(text, before, expected):
suggestions = suggest_type(text, before)
assert sorted_dicts(expected) == sorted_dicts(suggestions)
+
+
+def test_select_suggests_fields_from_function():
+ suggestions = suggest_type('SELECT FROM func()', 'SELECT ')
+ assert sorted_dicts(suggestions) == sorted_dicts([
+ {'type': 'column', 'tables': [(None, 'func', None, True)]},
+ {'type': 'function', 'schema': []},
+ {'type': 'keyword'}
+ ])