Skip to content

C# Quick Start: Parse SQL in .NET

To parse SQL in C#, install the gudusoft.gsqlparser NuGet package, create a TGSqlParser for your database dialect, assign sqltext, and call parse() — a return value of 0 means success and the parsed statements are in sqlstatements. In about 20 lines of C# you can validate T-SQL syntax, walk every statement in a script, and list the tables each statement touches.

This page gets you from zero to a working parser. The .NET edition supports SQL Server (T-SQL), Oracle (PL/SQL), MySQL, PostgreSQL, DB2, Snowflake, Redshift, Teradata, Hive, Impala, Informix, Netezza, Sybase, and Greenplum.

Prerequisites

  • .NET SDK (any modern version) — or .NET Framework 4.6.1+ via Visual Studio
  • The library targets .NET Standard 2.0, so it runs on Windows, Linux, and macOS

Installation

Install from NuGet:

1
dotnet add package gudusoft.gsqlparser

Or via the Package Manager console in Visual Studio:

1
Install-Package gudusoft.gsqlparser

Or as a PackageReference in your .csproj:

1
2
3
<ItemGroup>
    <PackageReference Include="gudusoft.gsqlparser" Version="*" />
</ItemGroup>

License setup

No license-activation code is required — there is no license key to embed and no SetLicense call to make. Add the package and start parsing. Evaluation use is limited to SQL scripts of up to 10,000 characters per parse; a commercial license removes this limit. See the licensing FAQ or sqlparser.com for editions and pricing.

Your First Parse (T-SQL)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using gudusoft.gsqlparser;

class QuickStart
{
    static void Main()
    {
        // Pick the dialect: dbvmssql = SQL Server / T-SQL
        var parser = new TGSqlParser(EDbVendor.dbvmssql);

        parser.sqltext = "SELECT TOP 10 id, name FROM dbo.employees WHERE dept_id = 5";

        int ret = parser.parse();   // 0 == success
        if (ret == 0)
        {
            Console.WriteLine("Parsed OK, statements: " + parser.sqlstatements.size());
            Console.WriteLine("First statement type: " + parser.sqlstatements.get(0).sqlstatementtype);
        }
        else
        {
            Console.WriteLine("Parse failed: " + parser.Errormessage);
        }
    }
}

Output:

1
2
Parsed OK, statements: 1
First statement type: sstselect

Notes on the API surface — the .NET edition mirrors the Java edition, so casing is mixed by design:

  • sqltext, parse(), sqlstatements, sqlstatementtype, tables keep their Java-style lowercase names
  • Errormessage, ErrorCount, SyntaxErrors, WhereClause, ResultColumnList, Statements are C# properties
  • Lists use size() and get(i) methods rather than indexers

To parse a file instead of a string, set parser.sqlfilename = @"C:\scripts\schema.sql"; (setting one input clears the other).

Check Syntax Errors

When parse() returns non-zero, SyntaxErrors holds one TSyntaxError per failed statement with the exact position of the problem:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using gudusoft.gsqlparser;

class CheckSyntax
{
    static void Main()
    {
        var parser = new TGSqlParser(EDbVendor.dbvmssql);
        parser.sqltext = "SELECT id, FROM orders WHERE;";   // broken on purpose

        if (parser.parse() != 0)
        {
            Console.WriteLine($"{parser.ErrorCount} error(s)");
            foreach (TSyntaxError e in parser.SyntaxErrors)
            {
                Console.WriteLine(
                    $"line {e.lineNo}, column {e.columnNo}, near '{e.tokentext}': {e.hint}");
            }
        }
    }
}

TSyntaxError exposes lineNo, columnNo, tokentext, hint, errortype, and errorno — everything an editor plugin or CI gate needs to point at the exact offending token. checkSyntax() is an alias of parse() if you prefer the intent-revealing name for validation-only scenarios.

Walk the Statements in a Script

A T-SQL script parses into a flat statement list — GO batch separators are handled automatically. Each statement reports its type via sqlstatementtype:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System;
using gudusoft.gsqlparser;

class WalkStatements
{
    static void Main()
    {
        var parser = new TGSqlParser(EDbVendor.dbvmssql);
        parser.sqltext =
            "CREATE TABLE dbo.orders (id INT, total MONEY)\n" +
            "GO\n" +
            "INSERT INTO dbo.orders VALUES (1, 99.50)\n" +
            "GO\n" +
            "SELECT TOP 10 * FROM dbo.orders";

        if (parser.parse() != 0)
        {
            Console.WriteLine(parser.Errormessage);
            return;
        }

        for (int i = 0; i < parser.sqlstatements.size(); i++)
        {
            TCustomSqlStatement stmt = parser.sqlstatements.get(i);
            Console.WriteLine($"[{i}] {stmt.sqlstatementtype}");
        }
    }
}

Statements can nest — stored procedure bodies, BEGIN...END blocks, IF branches. Recurse through stmt.Statements (the child-statement list on every statement) to reach them all, exactly like the Java getStatements() pattern in the PL/SQL guide.

Extract Table Names

Every statement exposes the tables it references through the tables list (table.FullName is schema-qualified; table.Name is the bare table name) — the basis for dependency inventories, access audits, and lineage:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using gudusoft.gsqlparser;
using gudusoft.gsqlparser.nodes;

class ExtractTables
{
    static void Main()
    {
        var parser = new TGSqlParser(EDbVendor.dbvmssql);
        parser.sqltext =
            "SELECT e.name, d.dept_name " +
            "FROM dbo.employees e " +
            "JOIN dbo.departments d ON e.dept_id = d.id " +
            "WHERE e.salary > 50000";

        if (parser.parse() != 0)
        {
            Console.WriteLine(parser.Errormessage);
            return;
        }

        ExtractFromStatement(parser.sqlstatements.get(0));
    }

    static void ExtractFromStatement(TCustomSqlStatement stmt)
    {
        for (int i = 0; i < stmt.tables.size(); i++)
        {
            TTable table = stmt.tables.getTable(i);
            Console.WriteLine("Table: " + table.FullName);
        }
        // recurse into nested statements (procedure bodies, blocks, ...)
        for (int i = 0; i < stmt.Statements.size(); i++)
        {
            ExtractFromStatement(stmt.Statements.get(i));
        }
    }
}

Output:

1
2
Table: dbo.employees
Table: dbo.departments

To go deeper — SELECT-list columns via select.ResultColumnList (cast to TSelectSqlStatement from gudusoft.gsqlparser.stmt), filter conditions via stmt.WhereClause, per-table column usage via table.LinkedColumns — the structure matches the Java docs one-to-one.

Switching Dialects

Pass a different EDbVendor to the constructor — the rest of your code is unchanged:

1
2
3
4
var oracle   = new TGSqlParser(EDbVendor.dbvoracle);      // Oracle & PL/SQL
var mysql    = new TGSqlParser(EDbVendor.dbvmysql);       // MySQL
var postgres = new TGSqlParser(EDbVendor.dbvpostgresql);  // PostgreSQL
var snow     = new TGSqlParser(EDbVendor.dbvsnowflake);   // Snowflake

Always match the vendor to the SQL's actual dialect: SELECT TOP 10 parses under dbvmssql but is a syntax error under dbvoracle.

Next Steps