Skip to content

Error Handling

Best practices for handling parse errors, understanding error messages, and recovering from parsing failures in GSP .NET.

The basics

TGSqlParser.parse() returns 0 on success and a non-zero integer on failure. Detailed errors are exposed through three properties:

Property Type Use it for
Errormessage string A pre-formatted multi-line error report. Good for logging.
SyntaxErrors IList<TSyntaxError> One entry per error, with line/column/token context.
ErrorCount int Quick count without iterating the list.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
using gudusoft.gsqlparser;

var parser = new TGSqlParser(EDbVendor.dbvoracle);
parser.sqltext = "SELECT * FROM"; // intentional syntax error

if (parser.parse() != 0)
{
    Console.Error.WriteLine($"Parse failed ({parser.ErrorCount} error(s)):");
    foreach (TSyntaxError err in parser.SyntaxErrors)
    {
        Console.Error.WriteLine(
            $"  line {err.lineNo}, col {err.columnNo}: " +
            $"unexpected '{err.tokentext}' ({err.errortype})");
    }
}

Per-statement error isolation

When a script contains multiple statements separated by ;, a syntax error in one statement does not abort the others. The parser still produces partial results for everything it could parse, and SyntaxErrors lists the specific failures.

Inside a stored procedure, however, a syntax error in one nested statement does abort the whole procedure — no AST will be built for the rest of that procedure body.

Distinguishing parse error from wrong-vendor error

The single most common cause of parse failures is using the wrong EDbVendor. Build a tiny helper:

1
2
3
4
5
6
7
8
9
public static (bool ok, EDbVendor? vendor) TryAllVendors(string sql)
{
    foreach (var v in Enum.GetValues<EDbVendor>())
    {
        var p = new TGSqlParser(v) { sqltext = sql };
        if (p.parse() == 0) return (true, v);
    }
    return (false, null);
}

If your input parses cleanly for some other vendor, the error is in the user's choice of vendor — surface that as an actionable hint in your UI.

Partial parsing

For interactive tools (linters, IDE plugins) that need an AST even from broken SQL, set EnablePartialParsing = true before calling parse(). The parser then attempts to build a best-effort AST around the syntax errors instead of bailing out.

1
2
3
4
5
parser.EnablePartialParsing = true;
parser.sqltext = "SELECT id FROM users WHERE";
parser.parse();
// parser.sqlstatements may still contain a TSelectSqlStatement with only
// the SELECT/FROM filled in.

Recoverable patterns

Retry with a different vendor

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static int ParseWithFallback(string sql, params EDbVendor[] vendors)
{
    foreach (var v in vendors)
    {
        var p = new TGSqlParser(v) { sqltext = sql };
        if (p.parse() == 0) return 0;
    }
    return -1;
}

// Usage: try Oracle first, fall back to MSSQL
ParseWithFallback(sql, EDbVendor.dbvoracle, EDbVendor.dbvmssql);

Strip non-standard prefixes

Some users paste SQL with a EXPLAIN, EXPLAIN PLAN FOR, or SQL> prompt prefix. Strip these before parsing:

1
sql = Regex.Replace(sql, @"^\s*(EXPLAIN(\s+PLAN\s+FOR)?|SQL>)\s+", "", RegexOptions.IgnoreCase);

Pre-validate identifier characters

If your input may contain forbidden characters (e.g. zero-width spaces from a chat client), normalise the string before parsing:

1
2
3
sql = sql
    .Replace("\u200B", "")  // ZERO WIDTH SPACE
    .Replace("\u00A0", " "); // NBSP

Logging error messages

For server-side workloads, log both Errormessage (for humans reading the log) and the structured SyntaxErrors (for machine processing):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
if (parser.parse() != 0)
{
    logger.LogWarning(
        "SQL parse failed for vendor {Vendor}: {Message}",
        parser.DbVendor,
        parser.Errormessage);

    foreach (TSyntaxError err in parser.SyntaxErrors)
        logger.LogDebug(
            "  at {Line}:{Col} -> '{Token}' ({Type})",
            err.lineNo, err.columnNo, err.tokentext, err.errortype);
}