Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Upcoming (TBD)
Bug fixes:
----------
* Restore cursor shape behaviour for Emacs mode
* Suggest columns, not datatypes, after a column literally named ``type`` in a ``SELECT`` list.

Features:
---------
Expand Down
25 changes: 24 additions & 1 deletion pgcli/packages/sqlcompletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,29 @@ def suggest_special(text):
return (Keyword(), Special())


def _is_datatype_keyword(token, stmt):
"""Decide whether a `type` token is the SQL TYPE keyword or a column name.

`type` is a non-reserved word in postgres, so sqlparse tags a column
literally named "type" as a keyword. The TYPE keyword only introduces a
datatype in DDL contexts (`ALTER COLUMN bar TYPE`, `SET DATA TYPE`,
`CREATE TYPE`), never directly inside a DML select/insert/update list.
"""
if isinstance(token, str):
# An explicit internal request for datatype suggestions, e.g. the
# parenthesized column list in `CREATE TABLE foo (bar <CURSOR>`.
return True

if not token.is_keyword:
return False

prev_keyword, _ = find_prev_keyword(stmt.text_before_cursor.rstrip(), n_skip=1)
if prev_keyword is None:
return False

return prev_keyword.ttype is not sqlparse.tokens.Keyword.DML


def suggest_based_on_last_token(token, stmt):
if isinstance(token, str):
token_v = token.lower()
Expand Down Expand Up @@ -492,7 +515,7 @@ def suggest_based_on_last_token(token, stmt):
return suggest_based_on_last_token(prev_keyword, stmt)
else:
return ()
elif token_v in ("type", "::"):
elif token_v == "::" or (token_v == "type" and _is_datatype_keyword(token, stmt)):
# ALTER TABLE foo SET DATA TYPE bar
# SELECT foo::bar
# Note that tables are a form of composite type in postgresql, so
Expand Down
17 changes: 17 additions & 0 deletions tests/test_sqlcompletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,23 @@ def test_alter_column_type_suggests_types():
}


@pytest.mark.parametrize(
"text",
[
"SELECT type ",
"SELECT type, ",
"SELECT id, type, ",
],
)
def test_column_named_type_still_suggests_columns(text):
# `type` is a non-reserved word, so sqlparse tags a column literally
# named "type" as the TYPE keyword; it must not switch the SELECT list
# over to datatype suggestions.
suggestions = suggest_type(text, text)
assert Column in {type(s) for s in suggestions}
assert Datatype(schema=None) not in suggestions


@pytest.mark.parametrize(
"text",
[
Expand Down
Loading