Support DuckDB syntax: FROM-first, SEMI/ANTI joins, COPY, ATTACH, MACRO, PRAGMA and DuckDB 2.0 additions - #2643
Conversation
Adds GLOB as a LikeExpression keyword so `col GLOB 'pattern'` and `col NOT GLOB 'pattern'` parse and deparse. GLOB stays a non-reserved keyword and remains usable as an identifier. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
DuckDB allows SEMI/ANTI JOIN without a LEFT prefix, and ANTI as a join type at all. ANTI previously parsed silently as a table alias, producing a wrong AST from valid SQL. Adds Join.isAnti(), the LEFT/RIGHT/FULL SEMI|ANTI prefixes and the standalone form, keeping ANTI usable as an identifier outside a join prefix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
DuckDB parameterises a type with other types, e.g. MAP(VARCHAR, INTEGER). Type arguments previously accepted identifiers and literals but not data type tokens. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
DuckDB matches an INSERT's source columns either positionally or by name. BY previously bound as a table alias, swallowing the clause. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
DuckDB writes the sample size first and the method behind it: USING SAMPLE 10%, USING SAMPLE 10 PERCENT (bernoulli), USING SAMPLE 10 ROWS (system, 377), USING SAMPLE reservoir (50 ROWS). The method is now optional, RESERVOIR is a known method, and a bare sample size accepts a unit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
DESCRIBE now accepts a query as well as a table, so DESCRIBE SELECT ..., DESCRIBE FROM ... and DESCRIBE WITH ... parse. The described query is reachable from TablesNamesFinder and rendered by the deparser. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds PRAGMA name, PRAGMA name(arguments) and PRAGMA name = value, as used by DuckDB and SQLite to read and set database settings. PRAGMA stays a non-reserved keyword. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds [FORCE] INSTALL extension [FROM repository] and LOAD extension, where the extension and repository may be an identifier or a string literal. INSTALL and LOAD stay non-reserved keywords. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
|
Run './gradlew spotlessApply' to fix all violations. |
Adds ATTACH [DATABASE] [IF NOT EXISTS] path [AS alias] [(options)] and DETACH [DATABASE] [IF EXISTS] name, with options such as READ_ONLY or TYPE SQLITE kept as strings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds CONNECT target [AS alias] and DISCONNECT [name], which route a session's queries to a remote database. Oracle's CONNECT BY is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds PREPARE name AS statement and DEALLOCATE [PREPARE] name. EXECUTE of a prepared statement already parsed. The prepared statement is a nested Statement, so TablesNamesFinder reaches its tables. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds COPY table [(columns)] FROM path [(options)] and
COPY {table | (query)} TO path [(options)]. Options such as
FORMAT PARQUET or AUTO_DETECT true are free-form keywords, so they are
collected verbatim. COPY stays valid as MySQL's ALGORITHM value.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds CREATE [OR REPLACE] [TEMPORARY] MACRO name (parameters) AS body for both scalar macros and table macros (AS TABLE SELECT ...). Parameters may carry a default value, as in add(a, b := 5). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds CREATE EXTENSION REPOSITORY [IF NOT EXISTS] name [WITH PREFIX prefix], which registers a custom repository to install extensions from. PostgreSQL's CREATE EXTENSION is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
DuckDB writes CREATE TRIGGER ... REFERENCING OLD TABLE AS o NEW TABLE AS n FOR EACH STATEMENT <statement>, i.e. the standard header with a SQL statement instead of EXECUTE FUNCTION. The standard-form parser now accepts a statement body, and such triggers are recognised and rendered as standard rather than MySQL-style triggers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
f74c137 to
a765cf7
Compare
|
@manticore-projects |
DuckDB lets the FROM clause lead, with the SELECT clause optional: FROM tbl SELECT a and FROM tbl WHERE a > 1. PlainSelect records the order in a fromFirst flag, and both the AST's own rendering and SelectDeParser emit the clauses accordingly. Bare FROM queries and the pipe syntax are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds the simplified statement form UNPIVOT tbl ON columns [INTO NAME name VALUE values] as UnPivotQuery, the counterpart of the existing PivotQuery. The FROM-clause form UNPIVOT (...) is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds JOIN t APPROX NEAREST n BY SIMILARITY expression, the vector similarity join of DuckDB 2.0. APPROX no longer binds as a table alias in that position. Also adds the lookahead the UNPIVOT statement's INTO clause needed to keep the grammar free of choice conflicts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
Adds WITH RECURSIVE tbl (a, b) USING KEY (a, avg(b)) AS (...), which aggregates a recursive CTE by key instead of accumulating every row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
a765cf7 to
c04286d
Compare
|
Please resolve conflicts. |
|
@manticore-projects |
…TA lookahead The merge of master resolved 26 conflict hunks by concatenating both sides while dropping the closing braces, @OverRide annotations and return statements that git had hoisted out of the conflict markers, leaving 6 visitor/deparser classes and the grammar unparseable. Also exclude LOAD DATA from the ExtensionStatement lookahead so BigQuery's LOAD DATA statement is not swallowed by DuckDB's LOAD extension alternative. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Merci beaucoups! |
|
@hayssams Thanks for this! I was waiting for this feature for more than a year now :) @manticore-projects Is it possible to release a new version with this feature, please? |
Done, though it may take a few minutes to appear on Maven Central. |
|
Thanks! |
|
Thanks @manticore-projects |
|
@manticore-projects I don't see any new release after 5.4 in Maven Central. Did the release process fail? |
Manticore Artifacts: https://central.sonatype.com/artifact/com.manticore-projects.jsqlformatter/jsqlparser |
|
@manticore-projects Why are there 2 namespaces, both being updated: Why is |
|
Because I run various projects depending on JSQLParser and depend often on the latest features, while @wumpz releases only from time to time. (There has been |
|
@guizmaii |
Adds support for nineteen DuckDB constructs that JSqlParser rejected (or silently mis-parsed), including the new DuckDB 2.0 syntax. One commit per feature, each with tests that assert both the AST and a stable round-trip through
toString()and the deparser.Features, with the DuckDB documentation that specifies them
GLOBpattern matchingWHERE b GLOB 'y*'SEMI/ANTIjoinsFROM t ANTI JOIN u ON t.id = u.idMAP(key, value)column typeCREATE TABLE t (m MAP(VARCHAR, INTEGER))INSERT ... BY NAME/BY POSITIONINSERT INTO t BY NAME SELECT 1 AS b, 2 AS aUSING SAMPLEsizes and methodsUSING SAMPLE 10 PERCENT (bernoulli)DESCRIBE <query>DESCRIBE SELECT * FROM tPRAGMAPRAGMA table_info('t'),PRAGMA memory_limit = '1GB'INSTALL/LOADextensionsFORCE INSTALL h3 FROM community,LOAD httpfsATTACH/DETACHATTACH 'file.db' AS mydb (TYPE SQLITE, READ_ONLY)PREPARE/DEALLOCATEPREPARE q AS SELECT * FROM t WHERE a = $1COPY(import and export)COPY (SELECT * FROM t) TO 'out.parquet' (FORMAT PARQUET)CREATE MACRO(scalar and table)CREATE MACRO add(a, b := 5) AS a + bFROM t SELECT a,FROM t WHERE a > 1UNPIVOTstatement formUNPIVOT monthly_sales ON jan, feb INTO NAME month VALUE salesCONNECT/DISCONNECTCONNECT 'postgres://localhost/mydb'CREATE TRIGGER ... REFERENCING OLD/NEW TABLEwith a statement bodyCREATE TRIGGER t AFTER UPDATE ON x REFERENCING OLD TABLE AS o NEW TABLE AS n FOR EACH STATEMENT INSERT INTO ...CREATE EXTENSION REPOSITORYCREATE EXTENSION REPOSITORY my_repo WITH PREFIX 'https://…'APPROX NEAREST n BY SIMILARITYjoinsJOIN products t APPROX NEAREST 2 BY SIMILARITY array_cosine_similarity(q.vec, t.vec)USING KEYWITH RECURSIVE tbl (a, b) USING KEY (a, avg(b)) AS (...)Notes
ANTI JOINpreviously parsed silently as a table alias, so valid SQL produced a wrong AST.Join.isAnti()now records it, andLEFT/RIGHT/FULL SEMI|ANTIare accepted too.Statementtypes (PRAGMA,INSTALL/LOAD,ATTACH/DETACH,CONNECT/DISCONNECT,PREPARE/DEALLOCATE,COPY,CREATE MACRO,CREATE EXTENSION REPOSITORY) are wired throughStatementVisitor,StatementVisitorAdapter,StatementDeParser,TablesNamesFinder,StatementFeatureVisitorandStatementValidator.COPY,PREPAREandUNPIVOTexpose their nested query toTablesNamesFinder.PlainSelectwith afromFirstflag, so the clause order round-trips;SelectDeParserfollows the same order. BareFROM tqueries and BigQuery pipe syntax are untouched.UnPivotQuerymirrors the existingPivotQuery, added asdefaultvisitor methods so no third-party visitor breaks.glob,anti,load,install,attach,detachandpragma.Test plan
DuckDBTest, each feature with a regression case for the syntax it could have shadowed (POSITION(...), MySQLALGORITHM COPY, OracleCONNECT BY, PostgreSQLCREATE EXTENSION,EXECUTE FUNCTIONtriggers, FROM-clauseUNPIVOT (...), pipe queries).SpecialOracleTest.testAllSqlsParseDeparse, which fails on an unmodifiedmasterin this environment too.0 errors and 1 warnings, the same warning as onmaster.🤖 Generated with Claude Code
https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2