Skip to content

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:

  1. Trusted application configuration selects the expected database dialect.
  2. GSP parses the complete SQL string or file with that dialect's grammar.
  3. Valid input returns the number of parsed statements.
  4. Invalid input returns the parser diagnostic and a failing process status.
  5. 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
SELECT TOP 5
       o.order_id,
       o.customer_id,
       o.total_amount
FROM sales.orders AS o
WHERE o.status = 'OPEN'
ORDER BY o.created_at DESC;

When parsed with the SQL Server dialect, both demos report:

1
2
3
4
Dialect: mssql
Database connection used: no
Result: ACCEPTED
Statements parsed: 1

The invalid example deliberately leaves the right side of the WHERE comparison incomplete:

1
2
3
4
5
6
SELECT TOP 5
       o.order_id,
       o.customer_id
FROM sales.orders AS o
WHERE o.status =
ORDER BY o.created_at DESC;

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
Result: REJECTED
Parser diagnostic:
syntax error, state:1744(10101) near: BY(6,7, token code:0)
1
2
3
Result: REJECTED
Parser diagnostic:
syntax error, state:541(10100) near: BY(6,7)

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
git clone --depth 1 https://github.com/sqlparser/gsp_demo_java.git
cd gsp_demo_java
mvn -q exec:java \
  -Dexec.mainClass=gudusoft.gsqlparser.demos.checksyntax.OfflineSyntaxCheck

The implementation is in OfflineSyntaxCheck.java, with behavior tests in OfflineSyntaxCheckTest.java.

1
2
3
4
git clone --depth 1 https://github.com/sqlparser/gsp_demo_dotnet.git
cd gsp_demo_dotnet
dotnet run --project \
  src/demos/checksyntax/demos.checksyntax.csproj -c Release

The implementation is in checksyntax.cs, with behavior tests in OfflineSyntaxCheckTests.cs.

Validate the checked-in SQL Server file:

1
2
3
mvn -q exec:java \
  -Dexec.mainClass=gudusoft.gsqlparser.demos.checksyntax.OfflineSyntaxCheck \
  -Dexec.args="/f samples/checksyntax/valid-mssql.sql /t mssql"
1
2
3
dotnet run --project \
  src/demos/checksyntax/demos.checksyntax.csproj -c Release -- \
  /f samples/checksyntax/valid-mssql.sql /t mssql

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
mvn -q -Dtest=OfflineSyntaxCheckTest test
1
dotnet test tests/checksyntax/test.checksyntax.csproj -c Release

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:

  1. Read the expected dialect from trusted configuration or request metadata.
  2. Apply input-size and request-rate limits before parsing untrusted content.
  3. Create a fresh parser and validate the complete SQL text.
  4. Return the diagnostic without attempting execution when parsing fails.
  5. Apply statement-count, statement-type, table, column, and function policy to accepted ASTs when the application needs those controls.
  6. Resolve database objects against metadata when object existence or type compatibility matters.
  7. 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