Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public enum Dialect {
AdjacentStringLiterals.WHITESPACE,
Feature.allowDoubleQuotedStrings,
Feature.allowBackslashEscapeCharacter), SNOWFLAKE(
Feature.allowBackslashEscapeCharacter), INFORMIX, SPANNER, DORIS, COCKROACHDB, TERADATA;
Feature.allowBackslashEscapeCharacter), INFORMIX, SPANNER, DORIS, COCKROACHDB, TERADATA, SQLITE;

private final Set<Feature> lexerFeatures;
private final AdjacentStringLiterals adjacentStringLiterals;
Expand Down
30 changes: 29 additions & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -3039,6 +3039,11 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
}
}

private void requireSqliteSyntax(String syntax) throws ParseException {
requireDdlSyntax(Dialect.SQLITE.name().equals(getAsString(Feature.dialect)),
syntax + " needs Dialect.SQLITE");
}

private void requireDdlSyntax(boolean valid, String message) throws ParseException {
if (!valid) {
throw new ParseException(message);
Expand Down Expand Up @@ -7966,7 +7971,30 @@ Upsert Upsert():
<K_REPLACE> { upsert.setUpsertType(UpsertType.REPLACE); }
|
(
<K_INSERT> <K_OR> <K_REPLACE> { upsert.setUpsertType(UpsertType.INSERT_OR_REPLACE); }
<K_INSERT> <K_OR>
(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do understand that this is SQLite specific syntax. If my understanding is correct, then please guard this block by the SQLite Dialect.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done in 8805d37. There was no SQLite value in Dialect, so I added Dialect.SQLITE and listed it in the dialect table in usage.rst.

INSERT OR IGNORE, ROLLBACK, ABORT and FAIL now parse only with Dialect.SQLITE; other dialects get a ParseException saying so. INSERT OR REPLACE still parses everywhere, because it did before this PR. The tests cover both cases, and the checks are green.

<K_REPLACE> { upsert.setUpsertType(UpsertType.INSERT_OR_REPLACE); }
|
// the other conflict resolution algorithms are SQLite syntax
<K_IGNORE> {
requireSqliteSyntax("INSERT OR IGNORE");
upsert.setUpsertType(UpsertType.INSERT_OR_IGNORE);
}
|
<K_ROLLBACK> {
requireSqliteSyntax("INSERT OR ROLLBACK");
upsert.setUpsertType(UpsertType.INSERT_OR_ROLLBACK);
}
|
// ABORT and FAIL are not keywords of the parser
tk=<S_IDENTIFIER> {
requireSqliteSyntax("INSERT OR " + tk.image);
requireDdlSyntax("ABORT".equalsIgnoreCase(tk.image) || "FAIL".equalsIgnoreCase(tk.image),
"Expected ABORT, FAIL, IGNORE, REPLACE or ROLLBACK");
upsert.setUpsertType("ABORT".equalsIgnoreCase(tk.image)
? UpsertType.INSERT_OR_ABORT : UpsertType.INSERT_OR_FAIL);
}
)
)
)
[ LOOKAHEAD(2) <K_INTO> { upsert.setUsingInto(true); } ]
Expand Down
2 changes: 2 additions & 0 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,8 @@ One grammar covers every supported RDBMS, but a few pieces of syntax mean differ
- ``ALTER TABLE ... ALTER PRIMARY KEY USING COLUMNS (...)`` with optional hash sharding and storage parameters
* - ``TERADATA``
- ``UPDATE target FROM sources SET ...`` with the FROM clause before SET
* - ``SQLITE``
- ``INSERT OR ABORT``, ``INSERT OR FAIL``, ``INSERT OR IGNORE`` and ``INSERT OR ROLLBACK`` conflict clauses

Features set explicitly *after* the preset win over it.

Expand Down
33 changes: 33 additions & 0 deletions src/test/java/net/sf/jsqlparser/statement/select/SQLiteTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,46 @@
package net.sf.jsqlparser.statement.select;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.test.TestUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.Locale;

import static org.junit.jupiter.api.Assertions.assertThrows;

public class SQLiteTest {
@Test
void testInsertOrReplaceUpsert() throws JSQLParserException {
String sqlString = "INSERT OR REPLACE INTO kjobLocks VALUES (?, ?, ?)";
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlString, true);
}

@ParameterizedTest
@ValueSource(strings = {"ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK"})
void testInsertOrConflictAlgorithm(String algorithm) throws JSQLParserException {
TestUtils.assertSqlCanBeParsedAndDeparsed(
"INSERT OR " + algorithm + " INTO t (a, b) VALUES (1, 2)", true,
parser -> parser.withDialect(Dialect.SQLITE));
TestUtils.assertSqlCanBeParsedAndDeparsed(
"insert or " + algorithm.toLowerCase(Locale.ROOT) + " into t select * from s", true,
parser -> parser.withDialect(Dialect.SQLITE));
}

@ParameterizedTest
@ValueSource(strings = {"ABORT", "FAIL", "IGNORE", "ROLLBACK"})
void testInsertOrConflictAlgorithmNeedsSqliteDialect(String algorithm) {
assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse("INSERT OR " + algorithm + " INTO t VALUES (1)"));
}

@Test
void testInsertOrUnknownAlgorithm() {
assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse("INSERT OR NOTHING INTO t VALUES (1)",
parser -> parser.withDialect(Dialect.SQLITE)));
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select time_id, product
, last_value(quantity ignore nulls) over (partition by product order by time_id) quantity
, last_value(quantity respect nulls) over (partition by product order by time_id) quantity
from ( select times.time_id, product, quantity
from inventory partition by (product)
right outer join times on (times.time_id = inventory.time_id)
where times.time_id between to_date('01/04/01', 'dd/mm/yy')
and to_date('06/04/01', 'dd/mm/yy'))
order by 2,1



--@FAILURE: Encountered unexpected token: "(" "(" recorded first on Aug 3, 2021, 7:20:07 AM
--@FAILURE: Encountered unexpected token: "by" "BY" recorded first on Apr 6, 2024, 7:38:53 AM
--@FAILURE: Encountered: <K_BY> / "by", at line 14, column 39, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select time_id, product
, last_value(quantity ignore nulls) over (partition by product order by time_id) quantity
, last_value(quantity respect nulls) over (partition by product order by time_id) quantity
from ( select times.time_id, product, quantity
from inventory partition by (product)
right outer join times on (times.time_id = inventory.time_id)
where times.time_id between to_date('01/04/01', 'dd/mm/yy')
and to_date('06/04/01', 'dd/mm/yy'))
order by 2,1



--@FAILURE: Encountered unexpected token: "(" "(" recorded first on Aug 3, 2021, 7:20:07 AM
--@FAILURE: Encountered unexpected token: "by" "BY" recorded first on Apr 6, 2024, 7:38:53 AM
--@FAILURE: Encountered: <K_BY> / "by", at line 14, column 39, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
--@FAILURE: --- recorded first on 23-Oct-2026, 4:26:44 pm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select times.time_id, product, quantity from inventory
partition by (product)
right outer join times on (times.time_id = inventory.time_id)
where times.time_id between to_date('01/04/01', 'dd/mm/yy')
and to_date('06/04/01', 'dd/mm/yy')
order by 2,1



--@FAILURE: Encountered unexpected token: "by" "BY" recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered: <K_BY> / "by", at line 11, column 14, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select times.time_id, product, quantity from inventory
partition by (product)
right outer join times on (times.time_id = inventory.time_id)
where times.time_id between to_date('01/04/01', 'dd/mm/yy')
and to_date('06/04/01', 'dd/mm/yy')
order by 2,1



--@FAILURE: Encountered unexpected token: "by" "BY" recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered: <K_BY> / "by", at line 11, column 14, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
--@FAILURE: --- recorded first on 23-Oct-2026, 4:26:44 pm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select count(*), max(scn)
from
(
(select sp.bo#, sp.pmoptype, sp.scn, sp.flags
from sumpartlog$ sp, sumdep$ sd
where sd.sumobj# = :1 and sd.p_obj# = sp.bo#
group by sp.bo#, sp.pmoptype, sp.scn, sp.flags)
minus
(select sp.bo#, sp.pmoptype, sp.scn, sp.flags
from sumpartlog$ sp
where sp.bo# not in
(
select sk.detailobj# from sumkey$ sk where sk.sumobj# = :1 and sk.detailcolfunction in (2,3)
)
and bitand(sp.flags, 2) != 2 and sp.pmoptype in (2,3,5,7))
group by sp.bo#, sp.pmoptype, sp.scn, sp.flags
)
where scn > :2

--@FAILURE: Encountered unexpected token: "group" "GROUP" recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered unexpected token: "minus" "MINUS" recorded first on Mar 25, 2023, 9:30:55 AM
--@FAILURE: Encountered: <K_GROUP> / "group", at line 25, column 2, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select count(*), max(scn)
from
(
(select sp.bo#, sp.pmoptype, sp.scn, sp.flags
from sumpartlog$ sp, sumdep$ sd
where sd.sumobj# = :1 and sd.p_obj# = sp.bo#
group by sp.bo#, sp.pmoptype, sp.scn, sp.flags)
minus
(select sp.bo#, sp.pmoptype, sp.scn, sp.flags
from sumpartlog$ sp
where sp.bo# not in
(
select sk.detailobj# from sumkey$ sk where sk.sumobj# = :1 and sk.detailcolfunction in (2,3)
)
and bitand(sp.flags, 2) != 2 and sp.pmoptype in (2,3,5,7))
group by sp.bo#, sp.pmoptype, sp.scn, sp.flags
)
where scn > :2

--@FAILURE: Encountered unexpected token: "group" "GROUP" recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered unexpected token: "minus" "MINUS" recorded first on Mar 25, 2023, 9:30:55 AM
--@FAILURE: Encountered: <K_GROUP> / "group", at line 25, column 2, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
--@FAILURE: --- recorded first on 23-Oct-2026, 4:26:44 pm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select *
from
(
select *
from "rme" "rm"
where "rm".a-interval:"sys_b_07" day(:"sys_b_08") to second(:"sys_b_09")
)

--@FAILURE: Encountered unexpected token: "(" "(" recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered: "(" / "(", at line 15, column 41, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: Encountered: <OPENING_BRACKET> / "(", at line 15, column 41, in lexical state DEFAULT. recorded first on 9 Jul 2025, 17:09:17
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select *
from
(
select *
from "rme" "rm"
where "rm".a-interval:"sys_b_07" day(:"sys_b_08") to second(:"sys_b_09")
)

--@FAILURE: Encountered unexpected token: "(" "(" recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered: "(" / "(", at line 15, column 41, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: Encountered: <OPENING_BRACKET> / "(", at line 15, column 41, in lexical state DEFAULT. recorded first on 9 Jul 2025, 17:09:17
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
--@FAILURE: --- recorded first on 23-Oct-2026, 4:26:44 pm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
update customers_demo
set cust_address_ntab = cust_address_ntab multiset union cust_address_ntab

--@FAILURE: Encountered unexpected token: "multiset" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered: <S_IDENTIFIER> / "multiset", at line 11, column 43, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
update customers_demo
set cust_address_ntab = cust_address_ntab multiset union cust_address_ntab

--@FAILURE: Encountered unexpected token: "multiset" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered: <S_IDENTIFIER> / "multiset", at line 11, column 43, in lexical state DEFAULT. recorded first on 23 May 2025, 22:04:10
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
--@FAILURE: --- recorded first on 23-Oct-2026, 4:26:44 pm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select customer_id, cust_address_ntab
multiset except distinct cust_address2_ntab multiset_except
from customers_demo

--@FAILURE: Encountered unexpected token: "except" "EXCEPT" recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered unexpected token: "distinct" "DISTINCT" recorded first on Mar 25, 2023, 9:18:30 AM
--@FAILURE: Encountered unexpected token: "cust_address2_ntab" <S_IDENTIFIER> recorded first on Feb 8, 2025, 6:46:21 AM
--@FAILURE: Encountered: <S_IDENTIFIER> / "cust_address2_ntab", at line 11, column 26, in lexical state DEFAULT. recorded first on 15 May 2025, 16:24:08
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
---
-- #%L
-- JSQLParser library
-- %%
-- Copyright (C) 2004 - 2019 JSQLParser
-- %%
-- Dual licensed under GNU LGPL 2.1 or Apache License 2.0
-- #L%
---
select customer_id, cust_address_ntab
multiset except distinct cust_address2_ntab multiset_except
from customers_demo

--@FAILURE: Encountered unexpected token: "except" "EXCEPT" recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered unexpected token: "distinct" "DISTINCT" recorded first on Mar 25, 2023, 9:18:30 AM
--@FAILURE: Encountered unexpected token: "cust_address2_ntab" <S_IDENTIFIER> recorded first on Feb 8, 2025, 6:46:21 AM
--@FAILURE: Encountered: <S_IDENTIFIER> / "cust_address2_ntab", at line 11, column 26, in lexical state DEFAULT. recorded first on 15 May 2025, 16:24:08
--@FAILURE: --- recorded first on 11 Apr 2026, 22:18:19
--@FAILURE: --- recorded first on 23-Oct-2026, 4:26:44 pm
Expand Down
Loading
Loading