Improving Materialize's errors #5340
Replies: 3 comments 2 replies
-
Your CoordError example has a variant with a |
Beta Was this translation helpful? Give feedback.
-
Can we have error codes as well? |
Beta Was this translation helpful? Give feedback.
-
Another thing I noticed regarding error messages is that when a construct gets desugared into some other form (in transform_ast.rs) and an error happens downstream the error that is surfaced to the user is in terms of the rewritten query. This can be confusing if the error references constructs that didn't appear in the original text. Here is an example from my recent
I'm not sure how deep a change this is, but ideally we'd have to remember the original SQL before de-sugaring so that we correctly display errors. I believe rustc handles the same problem by keeping around |
Beta Was this translation helpful? Give feedback.
-
Overview
I'd like us to adopt PostgreSQL's structured error strategy. The basic idea is summarized well by this error message from a recent version of PostgreSQL:
The key bit is the separation of the primary, detail, and hint fields. This allows for very consistent grammar and style for both simple and complicated errors.
Many of our more complicated errors jam a bunch of information into one line, often from dependencies with less than perfect error messages, and it ends up pretty awkward:
When you separate out the fields, the rendering becomes much more consistent. The primary field can always be a pithy, active voice sentence fragment, because you can jam as much information as you want into the detail field. And the hint field is always easily visible, because its the last thing printed.
The full PostgreSQL error message style guide is here: https://www.postgresql.org/docs/current/error-style-guide.html
Design
I believe the right course of action is to purge use of
anyhow::Error
in any code that's part of the core database engine.anyhow::Error
is still quite useful in utility code and tests, where Materialize developers are the consumer, rather than our users.I propose that we instead use structured error types everywhere. This increases the burden of introducing a new error, but it will vastly improve the quality of the generated error messages. Roughly:
We already do a limited version of this with the
EvalError
type in theexpr
crate, though it lacks the
detail
andhint
methods.pgwire
The one missing piece here is
"SQLSTATE", which
is a code specified by the SQL standard that identifies various classes of
errors. We can insulate most of the codebase from this nonsense by adding a
function like this to the
pgwire
crate:CLI
Introducing this error is also a good time to make our CLI errors properly
colorized.
Alternatives
One alternative is to introduce a
PgError
type with the right set of fields,and simply use this everywhere we currently use
anyhow::Error
:We'd probably want to introduce a macro to make constructing this type less
painful. This is essentially what PostgreSQL does internally, for what it's
worth.
The big downside of this solution is that it requires allocating at the point of
error construction. In Postgres, this doesn't much matter: as soon as a query
encounters an error, the entire query will be aborted. But in Materialize,
dataflows may accumulate many, many errors, and those errors all have to be
stored somewhere. Using an enum as the error type is therefore much cheaper,
memory-wise, as it's just a small integer that needs to be stored.
A smaller downside of this solution is that the whole codebase would be infected
with caring about a "PostgreSQL error" and its "SQLSTATE". The proposed solution
above would only require errors to care about "detail" and "hint" fields, which
are much more general puprose.
Beta Was this translation helpful? Give feedback.
All reactions