Parser Design Principles¶
General SQL Parser is built around a handful of principles that have stayed stable across the library's lifetime. Knowing them helps you reason about why the API looks the way it does, and what to expect from a release.
1. Per-vendor accuracy over universal coverage¶
Every supported dialect has its own lexer and parser, generated from a
vendor-specific .l/.y grammar source. There is no "ANSI core plus dialect
overlay" — Oracle SQL, T-SQL and Snowflake SQL are entirely separate grammars.
The trade-off:
- Pro: vendor-specific syntax (
CONNECT BY,TOP,QUALIFY) parses with the same accuracy as ANSI SQL. There is no second-class citizen. - Pro: a parse error always tells you whether the SQL is valid for the target dialect, not "valid SQL in the abstract".
- Con: far more grammar to maintain than a single-grammar parser. Mitigated by shared tooling and a regression suite that runs every grammar against thousands of fixture files.
2. Strongly-typed AST¶
Every SQL element gets its own class. There is no generic
Node { Type, Children } — there is TSelectSqlStatement, TFunctionCall,
TJoinExpr, and hundreds more, organised under the nodes and stmt packages
plus vendor-specific subpackages.
Consumer code walks the AST by type test rather than by string-matching node-type IDs:
1 2 3 4 5 6 7 | |
1 2 3 4 5 6 7 8 | |
The cost is a large type surface. The API reference is the canonical map.
3. Backwards compatibility¶
Public APIs — classes, methods, property names, enum members — are stable across
minor and patch releases. TGSqlParser.sqltext does not get renamed, and classes
do not move between packages without a deprecation cycle.
This costs some long-term cleanliness but is essential for consumers: most use cases involve traversing a deeply nested AST, so any name change ripples through their code. It is also why the .NET edition keeps Java-style lowercase member names where they were fields, instead of renaming everything to .NET conventions.
4. Grammar tables ship inside the library¶
All grammar tables for every supported dialect are embedded in the parser artifact itself. There is no data directory to deploy, no grammar file to distribute alongside, and no download at first use. That is what makes the library viable in air-gapped and regulated environments.
"Self-contained" is about grammars, not about having zero dependencies
The grammar tables are embedded, and the parser never connects to a database. But the published packages do declare dependencies, so plan your dependency closure accordingly:
- Java: the published
com.gudusoft:gsqlparser:4.1.6POM declaresjakarta.xml.bind-apiandjaxb-runtime, which resolve to six jars in total. JAXB is needed because the data-lineage APIs emit XML, and it was removed from the JDK in Java 11. - .NET: the
net10.0build has no dependencies. Thenetstandard2.0build depends onSystem.Text.Json8.0.5.
See offline / air-gapped installs for how to transfer the full closure.
5. Round-tripping over re-implementing¶
The parser is paired with a script writer that emits SQL from a possibly modified AST. The common case is consumers wanting to modify SQL — rename columns, swap dialects, redact constants — rather than only analyse it.
This is why visitors are a first-class citizen, why every node has a string representation, and why the AST is mutable in place.
6. Per-vendor isolation in the codebase¶
Per-vendor logic lives in per-vendor delegate classes and command tables rather
than in a central dispatcher. Changing how Oracle parses a WITH clause should
never affect SQL Server, and adding a vendor should not require editing shared
code.
The .NET build takes the same idea to the build level: each vendor is behind its own conditional-compilation flag, so a build can omit dialects it does not need.
7. Errors are recoverable¶
A syntax error in one statement of a multi-statement script does not abort the others. The parser collects errors and continues, which is what lets a linter or IDE plugin report every problem in a file rather than only the first.
Errors land in getSyntaxErrors(), one entry per problem, each with line,
column and token.
Errors land in the SyntaxErrors property, one entry per problem. Setting
EnablePartialParsing = true additionally builds a best-effort AST around a
broken statement.
8. Both editions track current runtimes¶
The parser JAR is Java 8 bytecode, so it runs on Java 8 through the latest LTS. Pin your compiler level to 1.8 so a modern JDK does not fall back to an unsupported default.
The library multi-targets net10.0 (recommended) and netstandard2.0 (the
compatibility facade for .NET Framework 4.6.2+, Mono and Unity). It does
not target netstandard2.1, netcoreapp*, or the EOL net5.0–net7.0
runtimes.
The .NET Framework floor is 4.6.2 rather than 4.6.1 because the
netstandard2.0 build depends on System.Text.Json 8.0.5, whose lowest .NET
Framework asset is net462.
See also¶
- Software Architecture for the component-level view
- Database Compatibility for vendor coverage details