|
75 | 75 |
|
76 | 76 | from psycopg import OperationalError, InterfaceError, Notify
|
77 | 77 | from psycopg.conninfo import make_conninfo, conninfo_to_dict
|
| 78 | +from psycopg.errors import Diagnostic |
78 | 79 |
|
79 | 80 | from collections import namedtuple
|
80 | 81 |
|
@@ -248,6 +249,9 @@ def __init__(
|
248 | 249 | )
|
249 | 250 |
|
250 | 251 | self.less_chatty = bool(less_chatty) or c["main"].as_bool("less_chatty")
|
| 252 | + self.verbose_errors = "verbose_errors" in c["main"] and c["main"].as_bool( |
| 253 | + "verbose_errors" |
| 254 | + ) |
251 | 255 | self.null_string = c["main"].get("null_string", "<null>")
|
252 | 256 | self.prompt_format = (
|
253 | 257 | prompt
|
@@ -389,6 +393,26 @@ def register_special_commands(self):
|
389 | 393 | "Echo a string to the query output channel.",
|
390 | 394 | )
|
391 | 395 |
|
| 396 | + self.pgspecial.register( |
| 397 | + self.toggle_verbose_errors, |
| 398 | + "\\v", |
| 399 | + "\\v [on|off]", |
| 400 | + "Toggle verbose errors.", |
| 401 | + ) |
| 402 | + |
| 403 | + def toggle_verbose_errors(self, pattern, **_): |
| 404 | + flag = pattern.strip() |
| 405 | + |
| 406 | + if flag == "on": |
| 407 | + self.verbose_errors = True |
| 408 | + elif flag == "off": |
| 409 | + self.verbose_errors = False |
| 410 | + else: |
| 411 | + self.verbose_errors = not self.verbose_errors |
| 412 | + |
| 413 | + message = "Verbose errors " + "on." if self.verbose_errors else "off." |
| 414 | + return [(None, None, None, message)] |
| 415 | + |
392 | 416 | def echo(self, pattern, **_):
|
393 | 417 | return [(None, None, None, pattern)]
|
394 | 418 |
|
@@ -1080,7 +1104,7 @@ def _evaluate_command(self, text):
|
1080 | 1104 | res = self.pgexecute.run(
|
1081 | 1105 | text,
|
1082 | 1106 | self.pgspecial,
|
1083 |
| - exception_formatter, |
| 1107 | + lambda x: exception_formatter(x, self.verbose_errors), |
1084 | 1108 | on_error_resume,
|
1085 | 1109 | explain_mode=self.explain_mode,
|
1086 | 1110 | )
|
@@ -1618,8 +1642,71 @@ def is_select(status):
|
1618 | 1642 | return status.split(None, 1)[0].lower() == "select"
|
1619 | 1643 |
|
1620 | 1644 |
|
1621 |
| -def exception_formatter(e): |
1622 |
| - return click.style(str(e), fg="red") |
| 1645 | +def diagnostic_output(diagnostic: Diagnostic) -> str: |
| 1646 | + fields = [] |
| 1647 | + |
| 1648 | + if diagnostic.severity is not None: |
| 1649 | + fields.append("Severity: " + diagnostic.severity) |
| 1650 | + |
| 1651 | + if diagnostic.severity_nonlocalized is not None: |
| 1652 | + fields.append("Severity (non-localized): " + diagnostic.severity_nonlocalized) |
| 1653 | + |
| 1654 | + if diagnostic.sqlstate is not None: |
| 1655 | + fields.append("SQLSTATE code: " + diagnostic.sqlstate) |
| 1656 | + |
| 1657 | + if diagnostic.message_primary is not None: |
| 1658 | + fields.append("Message: " + diagnostic.message_primary) |
| 1659 | + |
| 1660 | + if diagnostic.message_detail is not None: |
| 1661 | + fields.append("Detail: " + diagnostic.message_detail) |
| 1662 | + |
| 1663 | + if diagnostic.message_hint is not None: |
| 1664 | + fields.append("Hint: " + diagnostic.message_hint) |
| 1665 | + |
| 1666 | + if diagnostic.statement_position is not None: |
| 1667 | + fields.append("Position: " + diagnostic.statement_position) |
| 1668 | + |
| 1669 | + if diagnostic.internal_position is not None: |
| 1670 | + fields.append("Internal position: " + diagnostic.internal_position) |
| 1671 | + |
| 1672 | + if diagnostic.internal_query is not None: |
| 1673 | + fields.append("Internal query: " + diagnostic.internal_query) |
| 1674 | + |
| 1675 | + if diagnostic.context is not None: |
| 1676 | + fields.append("Where: " + diagnostic.context) |
| 1677 | + |
| 1678 | + if diagnostic.schema_name is not None: |
| 1679 | + fields.append("Schema name: " + diagnostic.schema_name) |
| 1680 | + |
| 1681 | + if diagnostic.table_name is not None: |
| 1682 | + fields.append("Table name: " + diagnostic.table_name) |
| 1683 | + |
| 1684 | + if diagnostic.column_name is not None: |
| 1685 | + fields.append("Column name: " + diagnostic.column_name) |
| 1686 | + |
| 1687 | + if diagnostic.datatype_name is not None: |
| 1688 | + fields.append("Data type name: " + diagnostic.datatype_name) |
| 1689 | + |
| 1690 | + if diagnostic.constraint_name is not None: |
| 1691 | + fields.append("Constraint name: " + diagnostic.constraint_name) |
| 1692 | + |
| 1693 | + if diagnostic.source_file is not None: |
| 1694 | + fields.append("File: " + diagnostic.source_file) |
| 1695 | + |
| 1696 | + if diagnostic.source_line is not None: |
| 1697 | + fields.append("Line: " + diagnostic.source_line) |
| 1698 | + |
| 1699 | + if diagnostic.source_function is not None: |
| 1700 | + fields.append("Routine: " + diagnostic.source_function) |
| 1701 | + |
| 1702 | + return "\n".join(fields) |
| 1703 | + |
| 1704 | + |
| 1705 | +def exception_formatter(e, verbose_errors: bool = False): |
| 1706 | + s = str(e) |
| 1707 | + if verbose_errors: |
| 1708 | + s += "\n" + diagnostic_output(e.diag) |
| 1709 | + return click.style(s, fg="red") |
1623 | 1710 |
|
1624 | 1711 |
|
1625 | 1712 | def format_output(title, cur, headers, status, settings, explain_mode=False):
|
|
0 commit comments