SqlForge 1.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package SqlForge --version 1.1.0
                    
NuGet\Install-Package SqlForge -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SqlForge" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SqlForge" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="SqlForge" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SqlForge --version 1.1.0
                    
#r "nuget: SqlForge, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SqlForge@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SqlForge&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=SqlForge&version=1.1.0
                    
Install as a Cake Tool

SqlForge

NuGet License: MIT .NET

A powerful SQL parser, AST generator, and query reconstructor library for .NET. SqlForge tokenizes SQL queries, builds an Abstract Syntax Tree (AST), and can reconstruct or format SQL from the AST representation.

Features

  • SQL Tokenization - Full lexical analysis with support for keywords, identifiers, literals, operators, and comments
  • AST Generation - Parse SQL into a traversable Abstract Syntax Tree
  • SQL Reconstruction - Rebuild valid SQL from AST nodes
  • SQL Formatting - Pretty-print AST for debugging and visualization
  • Dialect Support - SQL Anywhere and Microsoft SQL Server parsers, reconstructors, and formatters
  • Complex Query Support - Handles subqueries, JOINs, set operators, window functions, and nested expressions
  • MSSQL Extensions - TOP, OFFSET/FETCH, CTEs, APPLY, table hints, and window frames

Installation

NuGet Package Manager

Install-Package SqlForge

.NET CLI

dotnet add package SqlForge

PackageReference

<PackageReference Include="SqlForge" Version="1.1.0" />

Quick Start

using SqlForge.Parsers;
using SqlForge.Reconstructors;
using SqlForge.Formatters;

// Create parser with statement factory
var factory = new SqlAnywhereStatementParserFactory();
var parser = new SqlAnywhereParser(factory);

// Parse a SQL query
var ast = parser.Parse("SELECT id, name FROM users WHERE active = 1");

// Reconstruct the SQL
var reconstructor = new SqlAnywhereReconstructor();
string sql = reconstructor.Reconstruct(ast);
// Output: "SELECT id, name FROM users WHERE active = 1;"

// Format AST for debugging
var formatter = new SqlAnywhereFormatter();
string formatted = formatter.Format(ast);
using SqlForge.Parsers;
using SqlForge.Reconstructors;
using SqlForge.Formatters;

// MSSQL parser and reconstructor
var msFactory = new MsSqlServerStatementParserFactory();
var msParser = new MsSqlServerParser(msFactory);
var msAst = msParser.Parse("SELECT TOP 10 WITH TIES id FROM users ORDER BY id DESC");

var msReconstructor = new MsSqlServerReconstructor();
string msSql = msReconstructor.Reconstruct(msAst);

var msFormatter = new MsSqlServerFormatter();
string msFormatted = msFormatter.Format(msAst);

API Reference

Core Interfaces

Interface Description
ISqlParser Parses SQL strings into AST
ISqlReconstructor Reconstructs SQL from AST
ISqlFormatter Formats AST for human-readable output
ISqlNode Base interface for all AST nodes

Parsing

// Create the parser
var factory = new SqlAnywhereStatementParserFactory();
var parser = new SqlAnywhereParser(factory);

// Parse SQL into AST
SqlStatement ast = parser.Parse("SELECT * FROM customers");

// Access the statement body
if (ast.Body is SelectStatement select)
{
    Console.WriteLine($"Columns: {select.SelectItems.Count}");
    Console.WriteLine($"Has WHERE: {select.WhereClause != null}");
}
// Factory-based creation
var parserAny = SqlForgeFactory.CreateParser(SqlDialect.SqlAnywhere);
var parserMs = SqlForgeFactory.CreateParser(SqlDialect.MsSqlServer);

var reconAny = SqlForgeFactory.CreateReconstructor(SqlDialect.SqlAnywhere);
var reconMs = SqlForgeFactory.CreateReconstructor(SqlDialect.MsSqlServer);

var fmtAny = SqlForgeFactory.CreateFormatter(SqlDialect.SqlAnywhere);
var fmtMs = SqlForgeFactory.CreateFormatter(SqlDialect.MsSqlServer);

var ast1 = parserAny.Parse("SELECT id FROM users");
var ast2 = parserMs.Parse("SELECT TOP 5 id FROM users ORDER BY id DESC");

Reconstruction

var reconstructor = new SqlAnywhereReconstructor();

// Reconstruct with default dialect
string sql = reconstructor.Reconstruct(ast);

// Reconstruct with specific dialect
string sql = reconstructor.Reconstruct(ast, SqlDialect.SqlAnywhere);

Note: For Microsoft SQL Server, when an identifier was parsed as quoted but the original quote style is unknown, the reconstructor defaults to square brackets ([name]) to avoid reliance on QUOTED_IDENTIFIER.

Tokenization

using SqlForge.Utils;

var tokenizer = new Tokenizer("SELECT id FROM users");
List<Token> tokens = tokenizer.Tokenize();

foreach (var token in tokens)
{
    Console.WriteLine($"{token.Type}: {token.Value}");
}
// Output:
// Keyword: SELECT
// Identifier: id
// Keyword: FROM
// Identifier: users
// EOF:

AST Formatting

var formatter = new SqlAnywhereFormatter();
string output = formatter.Format(ast);

// Output (hierarchical representation):
// SqlStatement:
//     Type: Select
//     SelectStatement:
//         SelectItems:
//             SelectExpression:
//                 Expression:
//                     ColumnExpression:
//                         Name: id
//         FromClause:
//             TableExpressions:
//                 TableExpression:
//                     TableName: users

Supported SQL Features

Full examples for each dialect are in examples.md.

SELECT Statements

// Simple SELECT
parser.Parse("SELECT id, name FROM users");

// SELECT with aliases
parser.Parse("SELECT u.id AS user_id, u.name FROM users u");

// SELECT DISTINCT
parser.Parse("SELECT DISTINCT category FROM products");

// SELECT with expressions
parser.Parse("SELECT price * quantity AS total FROM orders");

WHERE Clause

// Simple conditions
parser.Parse("SELECT * FROM users WHERE active = 1");

// Complex conditions with AND/OR
parser.Parse("SELECT * FROM users WHERE active = 1 AND role = 'admin' OR id = 1");

// IN operator
parser.Parse("SELECT * FROM users WHERE id IN (1, 2, 3)");

// EXISTS subquery
parser.Parse("SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id)");

// LIKE operator
parser.Parse("SELECT * FROM users WHERE name LIKE 'John%'");

// IS NULL / IS NOT NULL
parser.Parse("SELECT * FROM users WHERE deleted_at IS NULL");

JOIN Operations

// INNER JOIN
parser.Parse(@"
    SELECT u.name, o.order_date
    FROM users u
    INNER JOIN orders o ON u.id = o.user_id
");

// LEFT OUTER JOIN
parser.Parse(@"
    SELECT u.name, o.order_date
    FROM users u
    LEFT OUTER JOIN orders o ON u.id = o.user_id
");

// Multiple JOINs
parser.Parse(@"
    SELECT u.name, o.order_date, p.product_name
    FROM users u
    INNER JOIN orders o ON u.id = o.user_id
    INNER JOIN products p ON o.product_id = p.id
");

// APPLY (MSSQL)
parser.Parse(@"
    SELECT u.id, x.last_order_id
    FROM users u
    CROSS APPLY (
        SELECT TOP 1 o.id AS last_order_id
        FROM orders o
        WHERE o.user_id = u.id
        ORDER BY o.id DESC
    ) x
");

Subqueries

// Subquery in SELECT
parser.Parse(@"
    SELECT name, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_count
    FROM users u
");

// Subquery in FROM (derived table)
parser.Parse(@"
    SELECT * FROM (SELECT id, name FROM users WHERE active = 1) AS active_users
");

// Subquery in WHERE
parser.Parse(@"
    SELECT * FROM products
    WHERE category_id IN (SELECT id FROM categories WHERE name = 'Electronics')
");

Set Operators

// UNION
parser.Parse("SELECT id FROM users UNION SELECT id FROM admins");

// UNION ALL
parser.Parse("SELECT id FROM users UNION ALL SELECT id FROM admins");

// EXCEPT
parser.Parse("SELECT id FROM users EXCEPT SELECT id FROM banned_users");

// INTERSECT
parser.Parse("SELECT id FROM users INTERSECT SELECT id FROM premium_users");

GROUP BY, HAVING, ORDER BY

// GROUP BY with aggregate functions
parser.Parse(@"
    SELECT category, COUNT(*) AS count, SUM(price) AS total
    FROM products
    GROUP BY category
");

// GROUP BY with HAVING
parser.Parse(@"
    SELECT category, COUNT(*) AS count
    FROM products
    GROUP BY category
    HAVING COUNT(*) > 10
");

// ORDER BY
parser.Parse("SELECT * FROM users ORDER BY name ASC, created_at DESC");

// OFFSET/FETCH (MSSQL)
parser.Parse("SELECT id FROM users ORDER BY id OFFSET 10 ROWS FETCH NEXT 20 ROWS ONLY");

Functions

// Aggregate functions
parser.Parse("SELECT COUNT(*), SUM(amount), AVG(price), MIN(date), MAX(date) FROM orders");

// Scalar functions
parser.Parse("SELECT SUBSTRING(name, 1, 10) FROM users");
parser.Parse("SELECT GETDATE()");

// Window functions (MSSQL)
parser.Parse("SELECT ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn FROM employees");
parser.Parse("SELECT SUM(amount) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM payments");

AST Node Types

Statement Nodes

Node Description
SqlStatement Root node containing statement type and body
SelectStatement SELECT statement with all clauses

Expression Nodes

Node Description
ColumnExpression Column reference (e.g., table.column)
LiteralExpression Literal values (string, number, null)
BinaryExpression Binary operations (e.g., a = b, a AND b)
UnaryExpression Unary operations (e.g., NOT x, EXISTS (...))
FunctionCallExpression Function calls (e.g., COUNT(*))
SubqueryExpression Subquery with optional alias
SetOperatorExpression Set operations (UNION, EXCEPT, INTERSECT)

Clause Nodes

Node Description
SelectExpression Column in SELECT with optional alias
FromClause FROM clause with table expressions
TableExpression Table reference with optional alias
JoinExpression JOIN operation with condition
WhereClause WHERE condition
GroupByClause GROUP BY expressions
HavingClause HAVING condition
OrderByClause ORDER BY items
OrderItem Single ORDER BY item with direction

Enums

SqlDialect

public enum SqlDialect
{
    Generic,
    SqlAnywhere,
    MsSqlServer,
    MySql,
    PostgreSql
}

TokenType

public enum TokenType
{
    Keyword,
    Identifier,
    StringLiteral,
    NumericLiteral,
    Operator,
    Parenthesis,
    Comma,
    Semicolon,
    EOF
}

JoinType

public enum JoinType
{
    Unknown,
    Inner,
    Left,
    Right,
    Full,
    Cross,
    Natural,
    CrossApply,
    OuterApply
}

QuoteStyle

public enum QuoteStyle
{
    None,
    DoubleQuotes,
    SquareBrackets
}

Error Handling

SqlForge throws SqlParseException for parsing errors:

try
{
    var ast = parser.Parse("SELECT FROM");  // Invalid SQL
}
catch (SqlParseException ex)
{
    Console.WriteLine($"Parse error at position {ex.Position}: {ex.Message}");
}

Thread Safety

Parser, reconstructor, and formatter instances are not thread-safe. Create new instances for concurrent operations or use synchronization.

Requirements

  • .NET Standard 2.0 or higher
  • Compatible with:
    • .NET Framework 4.6.1+
    • .NET Core 2.0+
    • .NET 5/6/7/8+

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

Syed Ikram Shah

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 63 2/12/2026
1.1.0 81 2/9/2026
1.0.0 84 2/8/2026