Skip to content

Commit 7eb65c8

Browse files
feat: guard INSERT OR conflict clauses by the SQLITE dialect
Add Dialect.SQLITE. INSERT OR ABORT, FAIL, IGNORE and ROLLBACK need it. INSERT OR REPLACE keeps parsing in every dialect.
1 parent 93c0f32 commit 7eb65c8

4 files changed

Lines changed: 33 additions & 7 deletions

File tree

‎src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public enum Dialect {
4848
AdjacentStringLiterals.WHITESPACE,
4949
Feature.allowDoubleQuotedStrings,
5050
Feature.allowBackslashEscapeCharacter), SNOWFLAKE(
51-
Feature.allowBackslashEscapeCharacter), INFORMIX, SPANNER, DORIS, COCKROACHDB, TERADATA;
51+
Feature.allowBackslashEscapeCharacter), INFORMIX, SPANNER, DORIS, COCKROACHDB, TERADATA, SQLITE;
5252

5353
private final Set<Feature> lexerFeatures;
5454
private final AdjacentStringLiterals adjacentStringLiterals;

‎src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3139,6 +3139,11 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
31393139
}
31403140
}
31413141

3142+
private void requireSqliteSyntax(String syntax) throws ParseException {
3143+
requireDdlSyntax(Dialect.SQLITE.name().equals(getAsString(Feature.dialect)),
3144+
syntax + " needs Dialect.SQLITE");
3145+
}
3146+
31423147
private void requireDdlSyntax(boolean valid, String message) throws ParseException {
31433148
if (!valid) {
31443149
throw new ParseException(message);
@@ -8119,12 +8124,20 @@ Upsert Upsert():
81198124
(
81208125
<K_REPLACE> { upsert.setUpsertType(UpsertType.INSERT_OR_REPLACE); }
81218126
|
8122-
<K_IGNORE> { upsert.setUpsertType(UpsertType.INSERT_OR_IGNORE); }
8127+
// the other conflict resolution algorithms are SQLite syntax
8128+
<K_IGNORE> {
8129+
requireSqliteSyntax("INSERT OR IGNORE");
8130+
upsert.setUpsertType(UpsertType.INSERT_OR_IGNORE);
8131+
}
81238132
|
8124-
<K_ROLLBACK> { upsert.setUpsertType(UpsertType.INSERT_OR_ROLLBACK); }
8133+
<K_ROLLBACK> {
8134+
requireSqliteSyntax("INSERT OR ROLLBACK");
8135+
upsert.setUpsertType(UpsertType.INSERT_OR_ROLLBACK);
8136+
}
81258137
|
8126-
// SQLite conflict resolution algorithms that are not keywords of the parser
8138+
// ABORT and FAIL are not keywords of the parser
81278139
tk=<S_IDENTIFIER> {
8140+
requireSqliteSyntax("INSERT OR " + tk.image);
81288141
requireDdlSyntax("ABORT".equalsIgnoreCase(tk.image) || "FAIL".equalsIgnoreCase(tk.image),
81298142
"Expected ABORT, FAIL, IGNORE, REPLACE or ROLLBACK");
81308143
upsert.setUpsertType("ABORT".equalsIgnoreCase(tk.image)

‎src/site/sphinx/usage.rst‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,6 +1604,8 @@ One grammar covers every supported RDBMS, but a few pieces of syntax mean differ
16041604
- ``ALTER TABLE ... ALTER PRIMARY KEY USING COLUMNS (...)`` with optional hash sharding and storage parameters
16051605
* - ``TERADATA``
16061606
- ``UPDATE target FROM sources SET ...`` with the FROM clause before SET
1607+
* - ``SQLITE``
1608+
- ``INSERT OR ABORT``, ``INSERT OR FAIL``, ``INSERT OR IGNORE`` and ``INSERT OR ROLLBACK`` conflict clauses
16071609

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

‎src/test/java/net/sf/jsqlparser/statement/select/SQLiteTest.java‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package net.sf.jsqlparser.statement.select;
1111

1212
import net.sf.jsqlparser.JSQLParserException;
13+
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
1314
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
1415
import net.sf.jsqlparser.test.TestUtils;
1516
import org.junit.jupiter.api.Test;
@@ -31,14 +32,24 @@ void testInsertOrReplaceUpsert() throws JSQLParserException {
3132
@ValueSource(strings = {"ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK"})
3233
void testInsertOrConflictAlgorithm(String algorithm) throws JSQLParserException {
3334
TestUtils.assertSqlCanBeParsedAndDeparsed(
34-
"INSERT OR " + algorithm + " INTO t (a, b) VALUES (1, 2)", true);
35+
"INSERT OR " + algorithm + " INTO t (a, b) VALUES (1, 2)", true,
36+
parser -> parser.withDialect(Dialect.SQLITE));
3537
TestUtils.assertSqlCanBeParsedAndDeparsed(
36-
"insert or " + algorithm.toLowerCase(Locale.ROOT) + " into t select * from s", true);
38+
"insert or " + algorithm.toLowerCase(Locale.ROOT) + " into t select * from s", true,
39+
parser -> parser.withDialect(Dialect.SQLITE));
40+
}
41+
42+
@ParameterizedTest
43+
@ValueSource(strings = {"ABORT", "FAIL", "IGNORE", "ROLLBACK"})
44+
void testInsertOrConflictAlgorithmNeedsSqliteDialect(String algorithm) {
45+
assertThrows(JSQLParserException.class,
46+
() -> CCJSqlParserUtil.parse("INSERT OR " + algorithm + " INTO t VALUES (1)"));
3747
}
3848

3949
@Test
4050
void testInsertOrUnknownAlgorithm() {
4151
assertThrows(JSQLParserException.class,
42-
() -> CCJSqlParserUtil.parse("INSERT OR NOTHING INTO t VALUES (1)"));
52+
() -> CCJSqlParserUtil.parse("INSERT OR NOTHING INTO t VALUES (1)",
53+
parser -> parser.withDialect(Dialect.SQLITE)));
4354
}
4455
}

0 commit comments

Comments
 (0)