Skip to content

Commit abca1ec

Browse files
committed
parserutils: extract shared PopulateErrorDetails into new package
This allows us to break the dependency from `jsonpath/parser` on `sql/parser`. Additionally, it fixes the bug where we used the hardcoded (inherited from `sql/parser`) code for ERROR token in other parsers - the method now explicitly takes that code as an argument. Release note: None
1 parent 5c029d5 commit abca1ec

File tree

14 files changed

+97
-57
lines changed

14 files changed

+97
-57
lines changed

pkg/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ ALL_TESTS = [
771771
"//pkg/util/json/tokenizer:tokenizer_test",
772772
"//pkg/util/json:json_disallowed_imports_test",
773773
"//pkg/util/json:json_test",
774+
"//pkg/util/jsonpath/parser:parser_disallowed_imports_test",
774775
"//pkg/util/jsonpath/parser:parser_test",
775776
"//pkg/util/limit:limit_test",
776777
"//pkg/util/log/eventlog:eventlog_test",
@@ -2164,6 +2165,7 @@ GO_TARGETS = [
21642165
"//pkg/sql/parser/statements:statements",
21652166
"//pkg/sql/parser:parser",
21662167
"//pkg/sql/parser:parser_test",
2168+
"//pkg/sql/parserutils:parserutils",
21672169
"//pkg/sql/pgrepl/lsn:lsn",
21682170
"//pkg/sql/pgrepl/lsnutil:lsnutil",
21692171
"//pkg/sql/pgrepl/pgreplparser:pgreplparser",

pkg/sql/parser/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ go_library(
2626
"//pkg/security/username", # keep
2727
"//pkg/sql/lexbase",
2828
"//pkg/sql/parser/statements",
29+
"//pkg/sql/parserutils",
2930
"//pkg/sql/pgwire/pgcode",
3031
"//pkg/sql/pgwire/pgerror",
3132
"//pkg/sql/privilege", # keep

pkg/sql/parser/lexer.go

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
package parser
77

88
import (
9-
"bytes"
109
"fmt"
1110
"strings"
1211

12+
"github.com/cockroachdb/cockroach/pkg/sql/parserutils"
1313
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
1414
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
1515
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
@@ -428,49 +428,9 @@ func (l *lexer) Error(e string) {
428428
l.populateErrorDetails()
429429
}
430430

431-
// PopulateErrorDetails properly wraps the "last error" field in the lexer.
432-
func PopulateErrorDetails(
433-
tokID int32, lastTokStr string, lastTokPos int32, lastErr error, lIn string,
434-
) error {
435-
var retErr error
436-
437-
if tokID == ERROR {
438-
// This is a tokenizer (lexical) error: the scanner
439-
// will have stored the error message in the string field.
440-
err := pgerror.WithCandidateCode(errors.Newf("lexical error: %s", lastTokStr), pgcode.Syntax)
441-
retErr = errors.WithSecondaryError(err, lastErr)
442-
} else {
443-
// This is a contextual error. Print the provided error message
444-
// and the error context.
445-
if !strings.Contains(lastErr.Error(), "syntax error") {
446-
// "syntax error" is already prepended when the yacc-generated
447-
// parser encounters a parsing error.
448-
lastErr = errors.Wrap(lastErr, "syntax error")
449-
}
450-
retErr = errors.Wrapf(lastErr, "at or near \"%s\"", lastTokStr)
451-
}
452-
453-
// Find the end of the line containing the last token.
454-
i := strings.IndexByte(lIn[lastTokPos:], '\n')
455-
if i == -1 {
456-
i = len(lIn)
457-
} else {
458-
i += int(lastTokPos)
459-
}
460-
// Find the beginning of the line containing the last token. Note that
461-
// LastIndexByte returns -1 if '\n' could not be found.
462-
j := strings.LastIndexByte(lIn[:lastTokPos], '\n') + 1
463-
// Output everything up to and including the line containing the last token.
464-
var buf bytes.Buffer
465-
fmt.Fprintf(&buf, "source SQL:\n%s\n", lIn[:i])
466-
// Output a caret indicating where the last token starts.
467-
fmt.Fprintf(&buf, "%s^", strings.Repeat(" ", int(lastTokPos)-j))
468-
return errors.WithDetail(retErr, buf.String())
469-
}
470-
471431
func (l *lexer) populateErrorDetails() {
472432
lastTok := l.lastToken()
473-
l.lastError = PopulateErrorDetails(lastTok.id, lastTok.str, lastTok.pos, l.lastError, l.in)
433+
l.lastError = parserutils.PopulateErrorDetails(lastTok.id, ERROR, lastTok.str, lastTok.pos, l.lastError, l.in)
474434
}
475435

476436
// SetHelp marks the "last error" field in the lexer to become a

pkg/sql/parserutils/BUILD.bazel

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "parserutils",
5+
srcs = ["utils.go"],
6+
importpath = "github.com/cockroachdb/cockroach/pkg/sql/parserutils",
7+
visibility = ["//visibility:public"],
8+
deps = [
9+
"//pkg/sql/pgwire/pgcode",
10+
"//pkg/sql/pgwire/pgerror",
11+
"@com_github_cockroachdb_errors//:errors",
12+
],
13+
)

pkg/sql/parserutils/utils.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2025 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the CockroachDB Software License
4+
// included in the /LICENSE file.
5+
6+
package parserutils
7+
8+
import (
9+
"bytes"
10+
"fmt"
11+
"strings"
12+
13+
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
14+
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
15+
"github.com/cockroachdb/errors"
16+
)
17+
18+
// PopulateErrorDetails properly wraps the "last error" field in the lexer.
19+
func PopulateErrorDetails(
20+
tokID, errTokenID int32, lastTokStr string, lastTokPos int32, lastErr error, lIn string,
21+
) error {
22+
var retErr error
23+
24+
if tokID == errTokenID {
25+
// This is a tokenizer (lexical) error: the scanner
26+
// will have stored the error message in the string field.
27+
err := pgerror.WithCandidateCode(errors.Newf("lexical error: %s", lastTokStr), pgcode.Syntax)
28+
retErr = errors.WithSecondaryError(err, lastErr)
29+
} else {
30+
// This is a contextual error. Print the provided error message
31+
// and the error context.
32+
if !strings.Contains(lastErr.Error(), "syntax error") {
33+
// "syntax error" is already prepended when the yacc-generated
34+
// parser encounters a parsing error.
35+
lastErr = errors.Wrap(lastErr, "syntax error")
36+
}
37+
retErr = errors.Wrapf(lastErr, "at or near \"%s\"", lastTokStr)
38+
}
39+
40+
// Find the end of the line containing the last token.
41+
i := strings.IndexByte(lIn[lastTokPos:], '\n')
42+
if i == -1 {
43+
i = len(lIn)
44+
} else {
45+
i += int(lastTokPos)
46+
}
47+
// Find the beginning of the line containing the last token. Note that
48+
// LastIndexByte returns -1 if '\n' could not be found.
49+
j := strings.LastIndexByte(lIn[:lastTokPos], '\n') + 1
50+
// Output everything up to and including the line containing the last token.
51+
var buf bytes.Buffer
52+
fmt.Fprintf(&buf, "source SQL:\n%s\n", lIn[:i])
53+
// Output a caret indicating where the last token starts.
54+
fmt.Fprintf(&buf, "%s^", strings.Repeat(" ", int(lastTokPos)-j))
55+
return errors.WithDetail(retErr, buf.String())
56+
}

pkg/sql/pgrepl/pgreplparser/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ go_library(
3737
visibility = ["//visibility:public"],
3838
deps = [
3939
"//pkg/sql/lexbase",
40-
"//pkg/sql/parser",
4140
"//pkg/sql/parser/statements",
41+
"//pkg/sql/parserutils",
4242
"//pkg/sql/pgrepl/lsn",
4343
"//pkg/sql/pgrepl/pgrepltree",
4444
"//pkg/sql/pgwire/pgcode",

pkg/sql/pgrepl/pgreplparser/lexer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"unicode/utf8"
1515

1616
"github.com/cockroachdb/cockroach/pkg/sql/lexbase"
17-
"github.com/cockroachdb/cockroach/pkg/sql/parser"
17+
"github.com/cockroachdb/cockroach/pkg/sql/parserutils"
1818
"github.com/cockroachdb/cockroach/pkg/sql/pgrepl/lsn"
1919
"github.com/cockroachdb/cockroach/pkg/sql/pgrepl/pgrepltree"
2020
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
@@ -73,7 +73,7 @@ func (l *lexer) Error(e string) {
7373
e = strings.TrimPrefix(e, "syntax error: ") // we'll add it again below.
7474
err := pgerror.WithCandidateCode(errors.Newf("%s", e), pgcode.Syntax)
7575
lastTok := l.lastToken
76-
l.lastError = parser.PopulateErrorDetails(lastTok.id, lastTok.str, lastTok.pos, err, l.in)
76+
l.lastError = parserutils.PopulateErrorDetails(lastTok.id, ERROR, lastTok.str, lastTok.pos, err, l.in)
7777
}
7878

7979
func (l *lexer) lex(lval *pgreplSymType) {

pkg/sql/pgrepl/pgreplparser/testdata/parser/start_replication.ddt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ expected a positive integer for timeline
3030
parse error
3131
START_REPLICATION SLOT slot_1 PHYSICAL A0010/1011 TIMELINE 3354731204578932752358923785325325
3232
----
33-
at or near "3354731204578932752358923785325325": syntax error
33+
lexical error: 3354731204578932752358923785325325
3434
DETAIL: source SQL:
3535
START_REPLICATION SLOT slot_1 PHYSICAL A0010/1011 TIMELINE 3354731204578932752358923785325325
3636
^

pkg/sql/pgrepl/pgreplparser/testdata/parser/timeline_history.ddt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ expected a positive integer for timeline
2626
parse error
2727
TIMELINE_HISTORY 133032475823895789234759378957238957328943289745381974981238947
2828
----
29-
at or near "133032475823895789234759378957238957328943289745381974981238947": syntax error
29+
lexical error: 133032475823895789234759378957238957328943289745381974981238947
3030
DETAIL: source SQL:
3131
TIMELINE_HISTORY 133032475823895789234759378957238957328943289745381974981238947
3232
^

pkg/sql/plpgsql/parser/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ go_library(
3737
"//pkg/build", # keep
3838
"//pkg/sql/parser",
3939
"//pkg/sql/parser/statements",
40+
"//pkg/sql/parserutils",
4041
"//pkg/sql/pgwire/pgcode",
4142
"//pkg/sql/pgwire/pgerror",
4243
"//pkg/sql/plpgsql/parser/lexbase", # keep

0 commit comments

Comments
 (0)