()
| 217 | } |
| 218 | |
| 219 | func (l *lexer) populateErrorDetails() { |
| 220 | lastTok := l.lastToken() |
| 221 | |
| 222 | if lastTok.id == ERROR { |
| 223 | // This is a tokenizer (lexical) error: the scanner |
| 224 | // will have stored the error message in the string field. |
| 225 | err := pgerror.WithCandidateCode(errors.Newf("lexical error: %s", lastTok.str), pgcode.Syntax) |
| 226 | l.lastError = errors.WithSecondaryError(err, l.lastError) |
| 227 | } else { |
| 228 | // This is a contextual error. Print the provided error message |
| 229 | // and the error context. |
| 230 | if !strings.Contains(l.lastError.Error(), "syntax error") { |
| 231 | // "syntax error" is already prepended when the yacc-generated |
| 232 | // parser encounters a parsing error. |
| 233 | l.lastError = errors.Wrap(l.lastError, "syntax error") |
| 234 | } |
| 235 | l.lastError = errors.Wrapf(l.lastError, "at or near \"%s\"", lastTok.str) |
| 236 | } |
| 237 | |
| 238 | // Find the end of the line containing the last token. |
| 239 | i := strings.IndexByte(l.in[lastTok.pos:], '\n') |
| 240 | if i == -1 { |
| 241 | i = len(l.in) |
| 242 | } else { |
| 243 | i += int(lastTok.pos) |
| 244 | } |
| 245 | // Find the beginning of the line containing the last token. Note that |
| 246 | // LastIndexByte returns -1 if '\n' could not be found. |
| 247 | j := strings.LastIndexByte(l.in[:lastTok.pos], '\n') + 1 |
| 248 | // Output everything up to and including the line containing the last token. |
| 249 | var buf bytes.Buffer |
| 250 | fmt.Fprintf(&buf, "source SQL:\n%s\n", l.in[:i]) |
| 251 | // Output a caret indicating where the last token starts. |
| 252 | fmt.Fprintf(&buf, "%s^", strings.Repeat(" ", int(lastTok.pos)-j)) |
| 253 | l.lastError = errors.WithDetail(l.lastError, buf.String()) |
| 254 | } |
| 255 | |
| 256 | // SetHelp marks the "last error" field in the lexer to become a |
| 257 | // help text. This method is invoked in the error action of the |
no test coverage detected