Module: PgReports::Connection::ErrorTranslator
- Defined in:
- lib/pg_reports/connection/error_translator.rb
Overview
Translates raw PG / ActiveRecord exceptions into human-readable messages with concrete remediation hints (typically a GRANT statement).
Usage:
PgReports::Connection::ErrorTranslator.translate(error)
# => { title: "...", detail: "...", hint: "GRANT ...", code: "42501" }
Class Method Summary collapse
- .auth_failed(_message) ⇒ Object
- .connection_refused(_message) ⇒ Object
- .database_does_not_exist(message) ⇒ Object
- .extract_object(message, regex) ⇒ Object
- .generic(error) ⇒ Object
- .permission_denied(message) ⇒ Object
- .sqlstate_for(error) ⇒ Object
- .too_many_connections(_message) ⇒ Object
-
.translate(error) ⇒ Object
Returns a Hash with :title, :detail, :hint, :code, :raw_message.
Class Method Details
.auth_failed(_message) ⇒ Object
71 72 73 74 75 76 77 |
# File 'lib/pg_reports/connection/error_translator.rb', line 71 def auth_failed() { title: "Authentication failed", detail: "PostgreSQL rejected the credentials for this target.", hint: "Verify the username/password in the target configuration; check pg_hba.conf for the connecting host." } end |
.connection_refused(_message) ⇒ Object
79 80 81 82 83 84 85 |
# File 'lib/pg_reports/connection/error_translator.rb', line 79 def connection_refused() { title: "Cannot reach PostgreSQL", detail: "The server is unreachable or refused the connection.", hint: "Check host/port, network reachability, and that PostgreSQL is accepting connections." } end |
.database_does_not_exist(message) ⇒ Object
62 63 64 65 66 67 68 69 |
# File 'lib/pg_reports/connection/error_translator.rb', line 62 def database_does_not_exist() target = extract_object(, /database "([^"]+)" does not exist/) { title: "Database not found", detail: target ? "PostgreSQL has no database named \"#{target}\"." : "The requested database does not exist on this server.", hint: nil } end |
.extract_object(message, regex) ⇒ Object
103 104 105 106 |
# File 'lib/pg_reports/connection/error_translator.rb', line 103 def extract_object(, regex) match = .match(regex) match && match[1] end |
.generic(error) ⇒ Object
95 96 97 98 99 100 101 |
# File 'lib/pg_reports/connection/error_translator.rb', line 95 def generic(error) { title: error.class.name.split("::").last, detail: error., hint: nil } end |
.permission_denied(message) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/pg_reports/connection/error_translator.rb', line 43 def () target = extract_object(, /permission denied for (?:database|schema|table|relation|view) "?([\w.]+)"?/) kind = extract_object(, /permission denied for (database|schema|table|relation|view)/) hint = if kind && target case kind when "database" then "GRANT CONNECT ON DATABASE #{target} TO <role>;" when "schema" then "GRANT USAGE ON SCHEMA #{target} TO <role>;" when "table", "relation", "view" then "GRANT SELECT ON #{target} TO <role>;" end end { title: "Permission denied", detail: (kind && target) ? "The connecting role does not have the required privilege on #{kind} \"#{target}\"." : "The connecting role lacks the required privilege.", hint: hint } end |
.sqlstate_for(error) ⇒ Object
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/pg_reports/connection/error_translator.rb', line 32 def sqlstate_for(error) case error when PG::Error error.result&.error_field(PG::Result::PG_DIAG_SQLSTATE) when ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotEstablished sqlstate_for(error.cause) if error.cause && !error.cause.equal?(error) end rescue nil end |
.too_many_connections(_message) ⇒ Object
87 88 89 90 91 92 93 |
# File 'lib/pg_reports/connection/error_translator.rb', line 87 def too_many_connections() { title: "Too many connections", detail: "PostgreSQL refused the connection because max_connections is reached.", hint: "Wait, increase max_connections, or use a connection pooler (PgBouncer)." } end |
.translate(error) ⇒ Object
Returns a Hash with :title, :detail, :hint, :code, :raw_message. The shape is suitable for rendering in the dashboard.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/pg_reports/connection/error_translator.rb', line 16 def translate(error) sqlstate = sqlstate_for(error) = error..to_s info = case sqlstate when "42501" then () when "3D000" then database_does_not_exist() when "28000", "28P01" then auth_failed() when "08001", "08006", "08000", "08003", "08004" then connection_refused() when "53300" then too_many_connections() else generic(error) end info.merge(code: sqlstate, raw_message: ) end |