Validate SQL syntax offline without a database¶
Use General SQL Parser (GSP) to validate SQL inside your application before the text reaches a database, deployment tool, or downstream analysis stage. Select the database dialect, parse the complete SQL input, and use the parser result to accept the input or return an actionable diagnostic to the caller.
The runnable Java and .NET projects work entirely in process. They require no database connection, credentials, schema access, or test database, so you can use the same pattern in a desktop editor, web service, CI runner, ETL pipeline, or isolated build environment.
View the Java source View the .NET source
What you will get
Each project provides a reusable validation method, a command-line interface,
valid and invalid SQL files, and six automated tests. The CLI returns exit code
0 for accepted SQL, 1 for a syntax rejection, and 2 for invalid input or
arguments, making it ready to use in build and deployment automation.
The problem this demo solves¶
SQL often enters a system before a database is available or before the application is willing to execute it. Examples include SQL typed into an editor, generated by an AI assistant, committed in a migration, supplied by a partner, or uploaded to an analytics product.
Without an offline validation step, a team may discover a syntax problem only after it opens a database connection and sends the statement. That delays feedback, couples validation to database availability and credentials, and makes it harder to use the same check in developer tools and CI.
The demo moves syntax validation earlier:
- Trusted application configuration selects the expected database dialect.
- GSP parses the complete SQL string or file with that dialect's grammar.
- Valid input returns the number of parsed statements.
- Invalid input returns the parser diagnostic and a failing process status.
- Only accepted SQL proceeds to later policy, metadata, review, or execution stages.
No query is sent to a database during these steps.
See the accept and reject paths¶
The checked-in valid SQL Server example contains TOP and bracketed
identifiers:
1 2 3 4 5 6 7 | |
When parsed with the SQL Server dialect, both demos report:
1 2 3 4 | |
The invalid example deliberately leaves the right side of the WHERE
comparison incomplete:
1 2 3 4 5 6 | |
GSP rejects it before any database call. The Java and .NET parser builds may use different internal parser state numbers, but both identify the token at line 6, column 7:
1 2 3 | |
1 2 3 | |
Treat the diagnostic as developer-facing detail. A user-facing product can map it to an editor marker, API validation response, CI annotation, or rejected-file report while preserving the original line and column information.
Why use a SQL parser for validation?¶
SQL syntax depends on grammar, statement boundaries, nesting, quoted identifiers, comments, procedural blocks, and database-specific extensions. A text search can detect a missing word it already knows about, but it cannot determine whether the complete token sequence is valid SQL for a particular database.
| Approach | What happens | Practical limitation |
|---|---|---|
| Send every candidate to a database | The database compiles or executes it | Requires connectivity and credentials and may introduce side effects or load |
| Split and inspect text manually | Application checks selected patterns | Breaks on strings, comments, delimiters, nested statements, and procedural SQL |
| Parse with a generic SQL grammar | Common syntax may be recognized | Vendor extensions can be rejected incorrectly or interpreted as another construct |
| Parse with the selected GSP dialect | GSP tokenizes and parses the complete input with a vendor grammar | Syntax validation still needs later semantic, authorization, and execution controls |
GSP gives the application a deterministic validation boundary without making the database itself part of that boundary.
How offline syntax validation works¶
flowchart LR
A[SQL text or file] --> B[Select expected database dialect]
B --> C[Create a fresh TGSqlParser]
C --> D[Tokenize and parse the complete input]
D --> E{Parse result}
E -- Invalid --> F[Return diagnostic and stop this workflow]
E -- Valid --> G[Return statement count and parsed AST]
G --> H[Continue to policy, metadata, review, or execution]
1. Select the expected dialect explicitly¶
The same text can be valid for one database and invalid for another. For
example, the demo's TOP query and bracketed identifiers are accepted with
EDbVendor.dbvmssql and rejected with EDbVendor.dbvoracle.
Choose the dialect from trusted project, connection, tenant, or file metadata. Do not silently fall back to Oracle or guess a dialect and then claim the SQL is valid for the user's intended database. If a product intentionally detects dialects, report the detected dialect separately from validation against the configured target.
2. Create a fresh parser for each validation¶
The demo creates a new TGSqlParser for every call. It assigns SQL through
sqltext, calls parse(), and reads the statement list or error message.
Keeping each request isolated prevents diagnostics and parser state from one
input from leaking into another.
For files, the CLI reads UTF-8 text and then calls the same reusable validation method. Your application can instead obtain SQL from an HTTP request, editor buffer, message, object store, repository, or generated-query component.
3. Parse the complete input¶
GSP understands statement boundaries, so the application does not need to
split a script on semicolons. When a valid script contains two statements, the
demo reports Statements parsed: 2.
Syntax validation does not decide whether multiple statements are allowed. A query endpoint can reject any count other than one, while a migration validator can accept a multi-statement script. Make that decision in the next policy stage, after parsing has identified the real statements.
4. Turn the result into an application decision¶
The reusable result contains:
| Result field | How to use it |
|---|---|
| Selected vendor | Record which grammar produced the decision |
| Valid or invalid | Allow the next workflow stage or return a rejection |
| Statement count | Apply endpoint or batch-specific statement-count policy |
| Parser diagnostic | Mark an editor location, annotate CI, or explain a rejected file |
The CLI exposes the same decision through stable result labels and exit codes.
Clone and run the complete demo¶
Choose the edition used by your application. Running without arguments checks a built-in Oracle query and demonstrates the accepted path.
1 2 3 4 | |
The implementation is in
OfflineSyntaxCheck.java,
with behavior tests in
OfflineSyntaxCheckTest.java.
1 2 3 4 | |
The implementation is in
checksyntax.cs,
with behavior tests in
OfflineSyntaxCheckTests.cs.
Validate the checked-in SQL Server file:
1 2 3 | |
1 2 3 | |
Replace valid-mssql.sql with invalid-mssql.sql to exercise the rejection
path. An exit code of 1 is expected for that file.
Run the focused tests after adapting the validator:
1 | |
1 | |
Where this pattern adds value¶
Give SQL editor and IDE users immediate feedback¶
An editor can validate the current buffer after a short debounce and mark the line and column returned by the parser. Users find missing keywords, parentheses, commas, or incomplete expressions before they open a database session or submit a query.
For partially typed SQL, distinguish an intermediate editor warning from a final submission failure. Keep the selected dialect visible because a query that is valid PostgreSQL may not be valid SQL Server or Oracle.
Block broken migrations and ETL scripts in CI¶
A CI job can scan changed .sql files, select the dialect from repository
configuration, and fail before deployment when any file is rejected. Record
the filename and diagnostic so the author can fix the script without searching
through a database deployment log.
This fits schema migrations, views, stored procedures, ETL transformations, scheduled reports, and checked-in query libraries. Use a fresh parser per file and aggregate every failure when the team benefits from a complete report.
Validate AI-generated and text-to-SQL output¶
Place syntax validation immediately after a model or query generator produces SQL. Invalid output can be returned for repair without consuming a database connection. Valid output can proceed to AST inspection, allowlists, tenant policy, cost controls, review, or execution.
Syntax success is only the first gate. A syntactically correct statement can still access a restricted table, modify data, omit tenant scope, or be too expensive. Combine this guide with the SQL AST policy and rewrite pattern when generated SQL may reach a database.
Validate SQL accepted by an API or ingestion pipeline¶
An analytics service, report builder, or partner-ingestion endpoint can reject malformed SQL at its API boundary. This produces consistent validation even when the database is temporarily unavailable and avoids distributing database credentials to every component that accepts SQL.
Return a structured application error containing the dialect, file or request identifier, and protected diagnostic. Do not expose sensitive SQL text in logs or responses unless the caller is authorized to see it.
Assess a SQL corpus before migration¶
A migration tool can parse a directory with the source dialect to establish a baseline, then classify rejected files for remediation. Repeating validation after a conversion or rewrite confirms that the generated text is still valid for the chosen parser dialect.
Syntax validation alone does not prove semantic equivalence between source and target databases. Join behavior, data types, functions, identifiers, and runtime results still need conversion rules and regression tests.
Integrate validation into your application¶
Use a small boundary with explicit outcomes:
- Read the expected dialect from trusted configuration or request metadata.
- Apply input-size and request-rate limits before parsing untrusted content.
- Create a fresh parser and validate the complete SQL text.
- Return the diagnostic without attempting execution when parsing fails.
- Apply statement-count, statement-type, table, column, and function policy to accepted ASTs when the application needs those controls.
- Resolve database objects against metadata when object existence or type compatibility matters.
- Execute only through a least-privilege database identity with parameter binding, timeouts, resource limits, and audit logging.
For batch processing, collect one result per file instead of stopping after the first error. Independent parser instances can be used by parallel workers; measure throughput with representative SQL before choosing concurrency and batch size.
What syntax validation does not prove¶
| Offline parsing can answer | It cannot answer by itself |
|---|---|
| Is this text valid for the selected GSP grammar? | Do every table, column, function, and type exist in this database? |
| Where did the parser encounter invalid syntax? | Is the caller authorized to access or modify those objects? |
| How many statements were parsed? | Should this application allow those statement types or that count? |
| What AST structure did the parser build? | Will the query return the intended rows or perform within a cost limit? |
| Can the workflow continue to deeper checks? | Is syntactically valid SQL safe from injection or abuse? |
Keep database-native compilation or test execution where exact server-version behavior matters. GSP moves syntax errors earlier and provides structured SQL for later controls; it does not replace the database's catalog, optimizer, permissions, or runtime behavior.
Common questions¶
Does offline SQL validation require database credentials?¶
No. The parser and its vendor grammars run inside the Java or .NET process. A database connection is needed only if your later workflow queries metadata or executes the accepted SQL.
Why does SQL pass for one dialect and fail for another?¶
Database products add their own keywords, quoting rules, statements, functions, and procedural syntax. Always validate against the dialect that will consume the SQL rather than a generic or guessed dialect.
Does a successful parse mean the SQL is safe to execute?¶
No. It means the syntax is valid for the selected grammar. Apply authorization, statement policy, object allowlists, parameter binding, tenant controls, least-privilege credentials, and resource limits before execution.
Can GSP validate multiple statements in one file?¶
Yes. The parser processes the complete script and exposes the parsed statement list. Your application decides whether a multi-statement script is allowed.
Can GSP confirm that tables and columns exist?¶
Syntax parsing identifies table and column references but does not know the contents of a particular database catalog automatically. Add metadata when you need object resolution, type checks, or database-version-specific compilation.
Can I use the same validator for generated SQL?¶
Yes. Pass the generated string directly to the reusable validation method. If it succeeds, continue to the policy and semantic checks required by your application rather than treating syntax success as approval to execute.
Next steps¶
- Use Error Handling for broader diagnostic and batch processing strategies.
- Use Working with Different Databases to
choose the correct
EDbVendor. - Add the SQL AST policy and rewrite layer when accepted SQL must be inspected or changed before execution.
- Review SQL syntax support by database for dialect-specific coverage and keyword references.