Errors and offsets
Parse Errors
If the SQL has some error while parsing then the result contains an Error object which provides details of the error.
using PgSqlParser;
var query = "SELEC 1";
var result = Parser.Parse(query);
if (result.Error is not null)
{
Console.WriteLine(result.Error.Message);
}
// Output: syntax error at or near "SELEC"error.Format(query) renders the error the way psql does, with the line it points at, and error.GetLineAndColumn(query) gives the position:
var query = "SELECT a,\n b FROM WHERE x";
var error = Parser.Parse(query).Error!;
Console.WriteLine(error.Format(query));
// Output:
// ERROR: syntax error at or near "WHERE"
// LINE 2: b FROM WHERE x
// ^The Error also carries CursorPos, the position of the error in the query (see Offsets and non-ASCII text), and the PostgreSQL source location that raised it in FuncName, FileName and LineNo.
Two kinds of invalid input are caught before the query reaches libpg_query:
- A
nullquery throwsArgumentNullException. - A query containing a NUL character (
\0) returns anErrorwith the messagequery contains a NUL character. PostgreSQL does not allow NUL in queries.
Deeply nested queries, such as a long chain of operators without parentheses (a || b || c ...) or many nested subqueries, are read on a dedicated thread with a large enough stack, so they cannot overflow the stack of the calling thread. A parse tree nested more than 4000 levels deep is rejected with an Error. The PostgreSQL parser has its own limit, which depends on the stack available to the calling thread and reports stack depth limit exceeded.
Protobuf's own recursive operations on a parse tree, such as ToString(), Clone() and Equals(), run on your thread. On a very deeply nested tree they can still overflow a small stack.
Offsets and non-ASCII text
.NET strings are UTF-16 while libpg_query works on UTF-8 bytes, so the unit of an offset depends on where it comes from:
ScantokenStart/EndandSplitStmt.Location/Lengthare UTF-16 offsets, ready to use withstring.Substring.- Parse tree locations (for example
RawStmt.StmtLocationand nodeLocation) andDeparseComment.MatchLocationare UTF-8 byte offsets. UseUtf8OffsetMapperto convert them:
using PgSqlParser.Utils;
var mapper = new Utf8OffsetMapper(query);
var charOffset = mapper.ToCharOffset(node.Location);It also converts the other way, for example to compare a position in the string (such as an editor caret) against parse tree locations:
var byteOffset = mapper.ToByteOffset(caretIndex);Error.CursorPosis a 1-based position in Unicode code points, as PostgreSQL reports it. UseError.GetCursorCharOffset(query)to get the matching string offset:
var result = Parser.Parse(query);
if (result.Error is { } error)
{
var charOffset = error.GetCursorCharOffset(query);
if (charOffset >= 0)
{
Console.WriteLine($"{error.Message}: {query[charOffset..]}");
}
}