Java SQL Parser Library

CI Status Coverage Status Codacy Status Manticore Build Maven Central Java Docs GitHub Issues Badge GitHub Stars

Turn any SQL statement into a traversable tree of Java objects — and back again.

JSQLParser is an RDBMS-agnostic SQL parser for the JVM, built with JavaCC: one grammar, all major dialects, no native extensions. Give it SQL, get an AST you can walk, rewrite and print back out.

Latest stable release: JSQLParser-5.3.340.jar

Development version: JSQLParser-5.4.340-SNAPSHOT.jar

Note

Since the 5.0 release JSQLParser requires Java 11 and has introduced new Visitors. Please see the Migration to 5.0 guide.

What it does

SELECT 1 FROM dual WHERE a = b
SQL Text
 └─Statements: statement.select.PlainSelect
    ├─selectItems: statement.select.SelectItem
    │  └─LongValue: 1
    ├─Table: dual
    └─where: expression.operators.relational.EqualsTo
       ├─Column: a
       └─Column: b
PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse("select 1 from dual where a=b");

Table table = (Table) select.getFromItem();
Assertions.assertEquals("dual", table.getName());

The tree is traversable with the Visitor pattern, and the same object model works in reverse: build statements from Java with a fluent API and render them as SQL text. See How to use it.

Install

Use the Manticore builds. They are immutable, versioned releases cut continuously from the current development line, and carry the grammar and performance work described below. The upstream com.github.jsqlparser release is considerably older.

<dependency>
    <groupId>com.manticore-projects.jsqlformatter</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>[5.3.218,)</version>
</dependency>

Upstream coordinates, snapshots and Gradle are on the Add JSQLParser to your Project page.

Performance

11× faster than 5.3, and the fastest parser on real-world SQL of any of the parsers tested, in any language — 19× ahead of sqlglot[c] on JSQLParser’s own SELECT test suite.

Benchmark                               (version)  Mode  Cnt   Score   Error  Units
JSQLParserBenchmark.parseSQLStatements     latest  avgt   15   7.602 ± 0.135  ms/op
JSQLParserBenchmark.parseSQLStatements        5.3  avgt   15  84.687 ± 3.321  ms/op

Methodology and the full cross-parser comparison against SQLGlot, sqlglot[c] and polyglot-sql: jsqlparser-bench.

What it parses

One grammar covers the SQL standard plus all major RDBMS. Missing syntax gets added on demand — open an issue.

BigQuery · Snowflake · DuckDB · Redshift · Oracle · MS SQL Server · Sybase · PostgreSQL · MySQL · MariaDB · DB2 · H2 · HSQLDB · Derby · SQLite

Statements

Queries

SELECT · WITH · Piped SQL

DML

INSERT · UPDATE · UPSERT · MERGE · DELETE · TRUNCATE TABLE

DDL

CREATE · ALTER · DROP

PostgreSQL RLS

CREATE POLICY · ALTER TABLE ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY

Salesforce SOQL

INCLUDES · EXCLUDES

Beyond statement shapes: nested sub-selects, bind parameters (?, :name), window and analytic functions, Oracle hints, the old Oracle JOIN (+), PostgreSQL implicit CAST ::, and the T-SQL square-bracket versus array-literal ambiguity. The complete reference is on the SQL Syntax 5.3.340 page; the gaps are on Unsupported Grammar of various RDBMS.

Statement classification

Any parsed statement can say what it actually does — no second parse, no visitor to write:

StatementFeatures features = CCJSqlParserUtil.parse(sqlStr).getFeatures();

// safeguard a read-only client before anything reaches the database
if (connection.isReadOnly() && features.mayModifyData()) {
    throw new SQLException("rejected: " + features.getUnresolvedReferences());
}

// dispatch correctly
if (features.returnsResultSet()) { statement.executeQuery(sqlStr);  }
else                             { statement.executeUpdate(sqlStr); }

This is not sqlStr.startsWith("SELECT") with extra steps: RETURNING turns DML into a row source, a data-modifying CTE hides a DELETE inside a SELECT, and INSERT INTO x SELECT .. contains a query but returns nothing. See Classify a Statement.

Piped SQL

Support is progressing for Piped SQL, which writes queries in the order they actually execute rather than the order SQL historically demanded.

FROM Produce
|> WHERE
    item != 'bananas'
    AND category IN ('fruit', 'nut')
|> AGGREGATE COUNT(*) AS num_items, SUM(sales) AS total_sales
   GROUP BY item
|> ORDER BY item DESC;

Background reading: the Google research paper, BigQuery pipe syntax and DuckDB FROM-first syntax.

Java version

JSQLParser

Runtime

Notes

4.9

JDK 8

last JDK 8 compatible release

5.0 and later

JDK 11

breaking changes to the AST Visitors, see Migration to 5.0

5.1 and later

JDK 11

building requires a JDK 17 toolchain (plugin requirement)

5.4 and later

JDK 11

parser generated with JavaCC 8

Sister projects

License

Dual licensed under LGPL 2.1 or the Apache License, Version 2.0. Take your pick.