Skip to content

How to Handle SQL Syntax Errors

When parse() returns a non-zero value, General SQL Parser gives you a precise, machine-readable error list: getSyntaxErrors() returns one TSyntaxError per failed statement, with the offending token, its line and column, a hint, and an error type. Because GSP validates statements independently, one bad statement does not stop the rest of a script from being checked — which is exactly what you need for batch validation in CI pipelines and SQL editors.

This guide covers reading TSyntaxError records, validating multi-statement scripts, and choosing an error-handling strategy.

The Basics: parse(), checkSyntax(), and Error Counts

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

public class BasicErrorHandling {
    public static void main(String[] args) {
        TGSqlParser parser = new TGSqlParser(EDbVendor.dbvmssql);
        parser.sqltext = "SELECT id, FROM orders WHERE;";

        int result = parser.parse();
        if (result != 0) {
            System.out.println("Errors: " + parser.getErrorCount());
            System.out.println(parser.getErrormessage());

            for (TSyntaxError error : parser.getSyntaxErrors()) {
                System.out.printf("line %d, column %d, near token '%s': %s%n",
                    error.lineNo, error.columnNo, error.tokentext, error.hint);
            }
        }
    }
}

Output:

1
2
3
Errors: 1
syntax error, state:2996(10101) near: ;(1,29, token code:0)
line 1, column 29, near token ';': syntax error, state:2996

Three related entry points:

  • parse() — returns 0 on success; non-zero means at least one statement failed. It also performs semantic analysis (table/column linking) on the successful statements.
  • checkSyntax() — an alias of parse(); both run the same pipeline. Use whichever name reads better in your code.
  • getErrorCount() / getErrormessage() — quick summary; getErrormessage() concatenates all errors into one printable string.

TSyntaxError Fields

getSyntaxErrors() returns an ArrayList<TSyntaxError>. The fields are public:

Field Type Meaning
tokentext String Text of the token where the error occurred
lineNo / columnNo long 1-based position of that token (getters getLineNo() / getColumnNo() also exist)
hint String Parser hint, e.g. syntax error, state:2996
errortype EErrorType Severity: sperror, spfatalerror, spwarning, sphint, ...
errorno int Numeric error code

error.getErrorMessage() formats a ready-to-print message from these fields. For editor integrations, lineNo/columnNo plus tokentext are enough to underline the exact spot in the buffer.

Warnings and hints

Entries with errortype of spwarning or sphint are advisories (for example, an orphan column that could not be linked to a table), not parse failures. Filter on errortype if you only want hard errors.

Validating Multi-Statement Scripts

GSP checks statements one at a time: a syntax error in statement 2 does not prevent statements 1 and 3 from parsing. Every parsed statement also carries its own error state — getErrorCount() and getSyntaxErrors() exist on TCustomSqlStatement too — so you can produce a per-statement validation report in a single pass:

 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
import gudusoft.gsqlparser.TGSqlParser;
import gudusoft.gsqlparser.EDbVendor;
import gudusoft.gsqlparser.TCustomSqlStatement;
import gudusoft.gsqlparser.TSyntaxError;

public class BatchValidator {
    public static void main(String[] args) {
        TGSqlParser parser = new TGSqlParser(EDbVendor.dbvoracle);
        parser.sqltext =
            "SELECT 1 FROM dual;\n" +
            "SELECT * FROM;\n" +          // broken
            "SELECT 2 FROM dual;";

        parser.parse(); // non-zero: at least one statement failed

        for (int i = 0; i < parser.getSqlstatements().size(); i++) {
            TCustomSqlStatement stmt = parser.getSqlstatements().get(i);
            long line = stmt.getStartToken() != null ? stmt.getStartToken().lineNo : -1;

            if (stmt.getErrorCount() == 0) {
                System.out.println("OK    line " + line + ": " + stmt.sqlstatementtype);
            } else {
                TSyntaxError e = stmt.getSyntaxErrors().get(0);
                System.out.println("ERROR line " + e.lineNo + ", col " + e.columnNo
                    + " near '" + e.tokentext + "'");
            }
        }
    }
}

Output:

1
2
3
OK    line 1: sstselect
ERROR line 2, col 14 near ';'
OK    line 3: sstselect

This pattern is the core of a CI gate: parse every changed .sql file, fail the build if any statement reports errors, and print file/line/column so the developer can jump straight to the problem. The same loop powers the online SQL Syntax Checker.

Reusing one parser across many inputs

parse() clears the previous error list on every call, so a single parser instance can validate many scripts in sequence — just assign new sqltext and parse again. Capture the results you need before the next call. See Performance Optimization for reuse and threading guidance.

1
2
3
4
boolean isValid(TGSqlParser parser, String sql) {
    parser.sqltext = sql;
    return parser.parse() == 0;
}

Choosing a Strategy

Fail fast (CI, migration gates). Treat any non-zero parse() as a hard failure and print getErrormessage(). Simplest and strictest.

Per-statement reporting (editors, linters). Use the batch pattern above: report each statement independently, keep analyzing the good ones. A single typo near the top of a 500-statement script should not suppress feedback on the other 499.

Tolerant extraction (inventory/lineage over legacy code). When mining metadata from a large legacy codebase, log failed statements for follow-up but continue processing every statement that parsed — getSqlstatements() contains the successful ones with full ASTs even when the overall return code is non-zero.

Statements inside stored procedures are the one place errors cascade: a syntax error inside a procedure body stops parsing of the rest of that procedure (the next top-level statement still parses). A legacy option setEnablePartialParsing(true) exists to continue past errors inside stored procedures, but it is deprecated and limited to Sybase procedure parsing — for other dialects, prefer isolating the failing procedure and validating its statements individually.

Common Causes of Parse Errors

  1. Wrong vendor. SELECT TOP 10 ... fails under dbvoracle; ROWNUM fails under dbvmssql. Always match EDbVendor to the SQL's dialect — see Working with Different Databases.
  2. Proprietary preprocessor syntax. Templating placeholders (${var}, Jinja) are not SQL; render them before parsing.
  3. Truly unsupported syntax. Check the SQL syntax support reference for your database, and report gaps via support — grammar coverage is updated continuously.