Skip to content

Commit

Permalink
Allow defining a json file with preferred aliases
Browse files Browse the repository at this point in the history
At $WORK we have a lot of tables with names like `foo_noun_verb` or
`foo_noun_related-noun_verb` and so while the default aliasing is very
helpful for shortening unwieldy names we do end up with lots of aliases
like `LEFT JOIN fnv on fnv2.id = fnv.fnv2_id`

This change will allow defining a json file of preferred aliases

```
> cat ~/.config/pgcli/aliases.json
{
    "foo_user": "user",
    "foo_user_group": "user_group"
}
```

so the alias suggestion for `SELECT * FROM foo_user` will be `SELECT * FROM foo_user AS user`
instead of the default `SELECT * FROM foo_user AS fu`
  • Loading branch information
Rob B committed Nov 11, 2022
1 parent 285e625 commit 9a086ef
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 3 deletions.
5 changes: 4 additions & 1 deletion changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ Upcoming
Features:
---------

* Changed the `destructive_warning` config to be a list of commands that are considered
* Changed the `destructive_warning` config to be a list of commands that are considered
destructive. This would allow you to be warned on `create`, `grant`, or `insert` queries.
* Destructive warnings will now include the alias dsn connection string name if provided (-D option).
* pgcli.magic will now work with connection URLs that use TLS client certificates for authentication
* Allow specifying an `alias_map_file` in the config that will use
predetermined table aliases instead of generating aliases programmatically on
the fly

3.5.0 (2022/09/15):
===================
Expand Down
1 change: 1 addition & 0 deletions pgcli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ def __init__(
"single_connection": single_connection,
"less_chatty": less_chatty,
"keyword_casing": keyword_casing,
"alias_map_file": c["main"]["alias_map_file"] or None,
}

completer = PGCompleter(
Expand Down
10 changes: 9 additions & 1 deletion pgcli/pgclirc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ multi_line_mode = psql

# Destructive warning will alert you before executing a sql statement
# that may cause harm to the database such as "drop table", "drop database",
# "shutdown", "delete", or "update".
# "shutdown", "delete", or "update".
# You can pass a list of destructive commands or leave it empty if you want to skip all warnings.
# "unconditional_update" will warn you of update statements that don't have a where clause
destructive_warning = drop, shutdown, delete, truncate, alter, update, unconditional_update
Expand All @@ -38,6 +38,14 @@ auto_expand = False
# If set to True, table suggestions will include a table alias
generate_aliases = False

# Path to a json file that specifies specific table aliases to use when generate_aliases is set to True
# the format for this file should be:
# {
# "some_table_name": "desired_alias",
# "some_other_table_name": "another_alias"
# }
alias_map_file = ""

# log_file location.
# In Unix/Linux: ~/.config/pgcli/log
# In Windows: %USERPROFILE%\AppData\Local\dbcli\pgcli\log
Expand Down
13 changes: 12 additions & 1 deletion pgcli/pgcompleter.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ def Candidate(
normalize_ref = lambda ref: ref if ref[0] == '"' else '"' + ref.lower() + '"'


def generate_alias(tbl):
def generate_alias(tbl, alias_map=None):
"""Generate a table alias, consisting of all upper-case letters in
the table name, or, if there are no upper-case letters, the first letter +
all letters preceded by _
param tbl - unescaped name of the table to alias
"""
if alias_map and tbl in alias_map:
return alias_map[tbl]
return "".join(
[l for l in tbl if l.isupper()]
or [l for l, prev in zip(tbl, "_" + tbl) if prev == "_" and l != "_"]
Expand Down Expand Up @@ -100,6 +102,15 @@ def __init__(self, smart_completion=True, pgspecial=None, settings=None):
self.call_arg_oneliner_max = settings.get("call_arg_oneliner_max", 2)
self.search_path_filter = settings.get("search_path_filter")
self.generate_aliases = settings.get("generate_aliases")

# when should this file be loaded? IO in constructors is not my preference but slow startup is
# probably better than slow first query
alias_map_file = settings.get("alias_map_file")
if alias_map_file is not None:
with open(alias_map_file) as fo:
self.alias_map = json.load(fo)
else:
self.alias_map = None
self.casing_file = settings.get("casing_file")
self.insert_col_skip_patterns = [
re.compile(pattern)
Expand Down

0 comments on commit 9a086ef

Please sign in to comment.