-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cli: promote the 5-character error codes in the error outputs
tldr: This patch opens a new era in the relationship between CockroachDB and its users by acknowledging the existence of SQLSTATE in the error outputs produced by CLI commands. Before: ``` root@127.0.0.1:61772/movr> select * from u; pq: relation "u" does not exist ``` After: ``` root@127.0.0.1:61772/movr> select * from u; ERROR: relation "u" does not exist SQLSTATE: 42P01 ``` Background: The SQL standard specifies that servers should signal errors to clients with both an explanatory text and a 5-character "SQLSTATE" code. Certain of these codes are even standardized. The purpose of these codes is to facilitate both troubleshooting by end-users and easier searching for resources about what to do under certain errors. PostgreSQL makes consistent use of these 5-digit codes; they are always communicated to clients as a special payload in the postgres wire protocol. In fact, CockroachDB has started ever since version 2.0 to imitate PostgreSQL and try an produce similar codes in similar circumstances. Unfortunately, CockroachDB's own SQL shell does not show SQLSTATE codes to the user and this fails to teach users about their existence. Moreover, users who build automation without knowing about SQLSTATE codes get tempted to rely on the text of the error message instead. This is undesirable because we wish to be able to improve error messages (to clarify them over time) and only promise more stability guarantees on the SQLSTATE. This patch improve supon this situation by promoting SQLSTATE in the default output of CLI commands. Additional reading resources: - https://www.postgresql.org/docs/12/errcodes-appendix.html - https://en.wikipedia.org/wiki/SQLSTATE Additionally, in order to prepare for the upcoming `pgx` migration, the `pq: ` prefix to error generated by `lib/pq` is stripped out. It is replaced by the "Severity" field of the pgwire error packet, if available. Release note (cli change): the various CLI commands that use SQL now display errors using a new display format that emphasizes the 5-digit SQLSTATE code. Users are encouraged to combine these codes together with the error message when seeking help or troubleshooting.
- Loading branch information
Showing
15 changed files
with
280 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Copyright 2019 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" | ||
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" | ||
"github.com/cockroachdb/cockroach/pkg/util/leaktest" | ||
"github.com/cockroachdb/errors" | ||
"github.com/lib/pq" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestOutputError(t *testing.T) { | ||
defer leaktest.AfterTest(t)() | ||
|
||
errBase := errors.New("woo") | ||
file, line, fn, _ := errors.GetOneLineSource(errBase) | ||
refLoc := fmt.Sprintf("%s, %s:%d", fn, file, line) | ||
testData := []struct { | ||
err error | ||
showSeverity, verbose bool | ||
exp string | ||
}{ | ||
// Check the basic with/without severity. | ||
{errBase, false, false, "woo"}, | ||
{errBase, true, false, "ERROR: woo"}, | ||
{pgerror.WithCandidateCode(errBase, pgcode.Syntax), false, false, "woo\nSQLSTATE: " + pgcode.Syntax}, | ||
// Check the verbose output. This includes the uncategorized sqlstate. | ||
{errBase, false, true, "woo\nSQLSTATE: " + pgcode.Uncategorized + "\nLOCATION: " + refLoc}, | ||
{errBase, true, true, "ERROR: woo\nSQLSTATE: " + pgcode.Uncategorized + "\nLOCATION: " + refLoc}, | ||
// Check the same over pq.Error objects. | ||
{&pq.Error{Message: "woo"}, false, false, "woo"}, | ||
{&pq.Error{Message: "woo"}, true, false, "ERROR: woo"}, | ||
{&pq.Error{Message: "woo"}, false, true, "woo"}, | ||
{&pq.Error{Message: "woo"}, true, true, "ERROR: woo"}, | ||
{&pq.Error{Severity: "W", Message: "woo"}, false, false, "woo"}, | ||
{&pq.Error{Severity: "W", Message: "woo"}, true, false, "W: woo"}, | ||
// Check hint printed after message. | ||
{errors.WithHint(errBase, "hello"), false, false, "woo\nHINT: hello"}, | ||
// Check sqlstate printed before hint, location after hint. | ||
{errors.WithHint(errBase, "hello"), false, true, "woo\nSQLSTATE: " + pgcode.Uncategorized + "\nHINT: hello\nLOCATION: " + refLoc}, | ||
// Check detail printed after message. | ||
{errors.WithDetail(errBase, "hello"), false, false, "woo\nDETAIL: hello"}, | ||
// Check hint/detail collection, hint printed after detail. | ||
{errors.WithHint( | ||
errors.WithDetail( | ||
errors.WithHint(errBase, "a"), | ||
"b"), | ||
"c"), false, false, "woo\nDETAIL: b\nHINT: a\n--\nc"}, | ||
{errors.WithDetail( | ||
errors.WithHint( | ||
errors.WithDetail(errBase, "a"), | ||
"b"), | ||
"c"), false, false, "woo\nDETAIL: a\n--\nc\nHINT: b"}, | ||
// Check sqlstate printed before detail, location after hint. | ||
{errors.WithDetail( | ||
errors.WithHint(errBase, "a"), "b"), | ||
false, true, "woo\nSQLSTATE: " + pgcode.Uncategorized + "\nDETAIL: b\nHINT: a\nLOCATION: " + refLoc}, | ||
} | ||
|
||
for _, tc := range testData { | ||
var buf strings.Builder | ||
cliOutputError(&buf, tc.err, tc.showSeverity, tc.verbose) | ||
assert.Equal(t, tc.exp+"\n", buf.String()) | ||
} | ||
} | ||
|
||
func TestFormatLocation(t *testing.T) { | ||
defer leaktest.AfterTest(t)() | ||
|
||
testData := []struct { | ||
file, line, fn string | ||
exp string | ||
}{ | ||
{"", "", "", ""}, | ||
{"a.b", "", "", "a.b"}, | ||
{"", "123", "", "<unknown>:123"}, | ||
{"", "", "abc", "abc"}, | ||
{"a.b", "", "abc", "abc, a.b"}, | ||
{"a.b", "123", "", "a.b:123"}, | ||
{"", "123", "abc", "abc, <unknown>:123"}, | ||
} | ||
|
||
for _, tc := range testData { | ||
r := formatLocation(tc.file, tc.line, tc.fn) | ||
assert.Equal(t, tc.exp, r) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.