Skip to content

Commit

Permalink
feat: Support unknown keyword (#2141)
Browse files Browse the repository at this point in the history
  • Loading branch information
EmilyOng authored Jan 16, 2025
1 parent df66569 commit 9ab1ebd
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.IsUnknownExpression;
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.Matches;
Expand Down Expand Up @@ -267,6 +268,12 @@ default void visit(IsBooleanExpression isBooleanExpression) {
this.visit(isBooleanExpression, null);
}

<S> T visit(IsUnknownExpression isUnknownExpression, S context);

default void visit(IsUnknownExpression isUnknownExpression) {
this.visit(isUnknownExpression, null);
}

<S> T visit(LikeExpression likeExpression, S context);

default void visit(LikeExpression likeExpression) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.IsUnknownExpression;
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.Matches;
Expand Down Expand Up @@ -263,6 +264,11 @@ public <S> T visit(IsBooleanExpression isBooleanExpression, S context) {
return isBooleanExpression.getLeftExpression().accept(this, context);
}

@Override
public <S> T visit(IsUnknownExpression isUnknownExpression, S context) {
return isUnknownExpression.getLeftExpression().accept(this, context);
}

@Override
public <S> T visit(LikeExpression likeExpression, S context) {
return visitBinaryExpression(likeExpression, context);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2025 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.expression.operators.relational;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;

public class IsUnknownExpression extends ASTNodeAccessImpl implements Expression {

private Expression leftExpression;
private boolean isNot = false;

public Expression getLeftExpression() {
return leftExpression;
}

public void setLeftExpression(Expression expression) {
leftExpression = expression;
}

public boolean isNot() {
return isNot;
}

public void setNot(boolean isNot) {
this.isNot = isNot;
}

@Override
public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
return expressionVisitor.visit(this, context);
}

@Override
public String toString() {
return leftExpression + " IS" + (isNot ? " NOT" : "") + " UNKNOWN";
}

public IsUnknownExpression withLeftExpression(Expression leftExpression) {
this.setLeftExpression(leftExpression);
return this;
}

public IsUnknownExpression withNot(boolean isNot) {
this.setNot(isNot);
return this;
}

public <E extends Expression> E getLeftExpression(Class<E> type) {
return type.cast(getLeftExpression());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ public class ParserKeywordsUtils {
{"UNBOUNDED", RESTRICTED_JSQLPARSER},
{"UNION", RESTRICTED_SQL2016},
{"UNIQUE", RESTRICTED_SQL2016},
{"UNKNOWN", RESTRICTED_SQL2016},
{"UNPIVOT", RESTRICTED_JSQLPARSER},
{"USE", RESTRICTED_JSQLPARSER},
{"USING", RESTRICTED_SQL2016},
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.IsUnknownExpression;
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.Matches;
Expand Down Expand Up @@ -530,6 +531,12 @@ public <S> Void visit(IsBooleanExpression isBooleanExpression, S context) {
return null;
}

@Override
public <S> Void visit(IsUnknownExpression isUnknownExpression, S context) {

return null;
}

@Override
public <S> Void visit(JdbcParameter jdbcParameter, S context) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.IsUnknownExpression;
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.Matches;
Expand Down Expand Up @@ -428,6 +429,17 @@ public <S> StringBuilder visit(IsBooleanExpression isBooleanExpression, S contex
return buffer;
}

@Override
public <S> StringBuilder visit(IsUnknownExpression isUnknownExpression, S context) {
isUnknownExpression.getLeftExpression().accept(this, context);
if (isUnknownExpression.isNot()) {
buffer.append(" IS NOT UNKNOWN");
} else {
buffer.append(" IS UNKNOWN");
}
return buffer;
}

@Override
public <S> StringBuilder visit(JdbcParameter jdbcParameter, S context) {
buffer.append(jdbcParameter.getParameterCharacter());
Expand Down Expand Up @@ -514,6 +526,10 @@ public void visit(IsBooleanExpression isBooleanExpression) {
visit(isBooleanExpression, null);
}

public void visit(IsUnknownExpression isUnknownExpression) {
visit(isUnknownExpression, null);
}

public void visit(JdbcParameter jdbcParameter) {
visit(jdbcParameter, null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.IsUnknownExpression;
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.Matches;
Expand Down Expand Up @@ -289,6 +290,12 @@ public <S> Void visit(IsBooleanExpression isBooleanExpression, S context) {
return null;
}

@Override
public <S> Void visit(IsUnknownExpression isUnknownExpression, S context) {
isUnknownExpression.getLeftExpression().accept(this, context);
return null;
}

@Override
public <S> Void visit(JdbcParameter jdbcParameter, S context) {
validateFeature(Feature.jdbcParameter);
Expand Down Expand Up @@ -383,6 +390,10 @@ public void visit(IsBooleanExpression isBooleanExpression) {
visit(isBooleanExpression, null); // Call the parametrized visit method with null context
}

public void visit(IsUnknownExpression isUnknownExpression) {
visit(isUnknownExpression, null); // Call the parametrized visit method with null context
}

public void visit(JdbcParameter jdbcParameter) {
visit(jdbcParameter, null); // Call the parametrized visit method with null context
}
Expand Down
18 changes: 18 additions & 0 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
| <K_UNBOUNDED: "UNBOUNDED">
| <K_UNION:"UNION">
| <K_UNIQUE:"UNIQUE">
| <K_UNKNOWN:"UNKNOWN">
| <K_UNLOGGED: "UNLOGGED">
| <K_UNPIVOT:"UNPIVOT">
| <K_UPDATE:"UPDATE">
Expand Down Expand Up @@ -4153,6 +4154,8 @@ Expression SQLCondition():
|
LOOKAHEAD(IsBooleanExpression()) result=IsBooleanExpression(left)
|
LOOKAHEAD(IsUnknownExpression()) result=IsUnknownExpression(left)
|
LOOKAHEAD(2) result=LikeExpression(left)
|
LOOKAHEAD(IsDistinctExpression()) result=IsDistinctExpression(left)
Expand Down Expand Up @@ -4366,6 +4369,21 @@ Expression IsBooleanExpression(Expression leftExpression):
}
}

Expression IsUnknownExpression(Expression leftExpression):
{
IsUnknownExpression result = new IsUnknownExpression();
}
{
(
<K_IS> [<K_NOT> { result.setNot(true); } ] <K_UNKNOWN>
)

{
result.setLeftExpression(leftExpression);
return result;
}
}

Expression ExistsExpression():
{
ExistsExpression result = new ExistsExpression();
Expand Down
12 changes: 8 additions & 4 deletions src/site/sphinx/keywords.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and
+----------------------+-------------+-----------+
| QUALIFY | Yes | |
+----------------------+-------------+-----------+
| HAVING | Yes | Yes |
| HAVING | Yes | Yes |
+----------------------+-------------+-----------+
| IF | Yes | Yes |
+----------------------+-------------+-----------+
Expand All @@ -97,7 +97,7 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and
+----------------------+-------------+-----------+
| INTERVAL | Yes | Yes |
+----------------------+-------------+-----------+
| INTO | Yes | Yes |
| INTO | Yes | Yes |
+----------------------+-------------+-----------+
| IS | Yes | Yes |
+----------------------+-------------+-----------+
Expand All @@ -109,7 +109,7 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and
+----------------------+-------------+-----------+
| LIKE | Yes | Yes |
+----------------------+-------------+-----------+
| LIMIT | Yes | Yes |
| LIMIT | Yes | Yes |
+----------------------+-------------+-----------+
| MINUS | Yes | Yes |
+----------------------+-------------+-----------+
Expand Down Expand Up @@ -139,7 +139,9 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and
+----------------------+-------------+-----------+
| OPTIMIZE | Yes | Yes |
+----------------------+-------------+-----------+
| PIVOT | Yes | Yes |
| OVERWRITE | Yes | Yes |
+----------------------+-------------+-----------+
| PIVOT | Yes | Yes |
+----------------------+-------------+-----------+
| PREFERRING | Yes | Yes |
+----------------------+-------------+-----------+
Expand Down Expand Up @@ -181,6 +183,8 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and
+----------------------+-------------+-----------+
| UNIQUE | Yes | Yes |
+----------------------+-------------+-----------+
| UNKNOWN | Yes | Yes |
+----------------------+-------------+-----------+
| UNPIVOT | Yes | Yes |
+----------------------+-------------+-----------+
| USE | Yes | Yes |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2023 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.expression.operators.relational;

import net.sf.jsqlparser.schema.Column;
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 static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

class IsUnknownExpressionTest {

@ParameterizedTest
@ValueSource(strings = {
"SELECT * FROM mytable WHERE 1 IS UNKNOWN",
"SELECT * FROM mytable WHERE 1 IS NOT UNKNOWN",
})
public void testIsUnknownExpression(String sqlStr) {
assertDoesNotThrow(() -> TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr));
}

@Test
void testStringConstructor() {
Column column = new Column("x");

IsUnknownExpression defaultIsUnknownExpression =
new IsUnknownExpression().withLeftExpression(column);
TestUtils.assertExpressionCanBeDeparsedAs(defaultIsUnknownExpression, "x IS UNKNOWN");

IsUnknownExpression isUnknownExpression =
new IsUnknownExpression().withLeftExpression(column).withNot(false);
TestUtils.assertExpressionCanBeDeparsedAs(isUnknownExpression, "x IS UNKNOWN");

IsUnknownExpression isNotUnknownExpression =
new IsUnknownExpression().withLeftExpression(column).withNot(true);
TestUtils.assertExpressionCanBeDeparsedAs(isNotUnknownExpression, "x IS NOT UNKNOWN");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public class ReflectionModelTest {
new net.sf.jsqlparser.expression.operators.relational.InExpression(),
new net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression(),
new net.sf.jsqlparser.expression.operators.relational.IsNullExpression(),
new net.sf.jsqlparser.expression.operators.relational.IsUnknownExpression(),
new net.sf.jsqlparser.expression.operators.relational.JsonOperator("@>"),
new net.sf.jsqlparser.expression.operators.relational.LikeExpression(),
new net.sf.jsqlparser.expression.operators.relational.Matches(),
Expand Down
12 changes: 12 additions & 0 deletions src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2271,6 +2271,18 @@ public void testIsNotFalse() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed(statement);
}

@Test
public void testIsUnknown() throws JSQLParserException {
String statement = "SELECT col FROM tbl WHERE col IS UNKNOWN";
assertSqlCanBeParsedAndDeparsed(statement);
}

@Test
public void testIsNotUnknown() throws JSQLParserException {
String statement = "SELECT col FROM tbl WHERE col IS NOT UNKNOWN";
assertSqlCanBeParsedAndDeparsed(statement);
}

@Test
public void testTSQLJoin() throws JSQLParserException {
String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a *= tabelle2.b";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ public void testIsNull() {
validateNoErrors("SELECT * FROM tab t WHERE t.col IS NOT NULL", 1, EXPRESSIONS);
}

@Test
public void testIsUnknown() {
validateNoErrors("SELECT * FROM tab t WHERE t.col IS UNKNOWN", 1, EXPRESSIONS);
validateNoErrors("SELECT * FROM tab t WHERE t.col IS NOT UNKNOWN", 1, EXPRESSIONS);
}

@Test
public void testLike() {
validateNoErrors("SELECT * FROM tab t WHERE t.col LIKE '%search for%'", 1, EXPRESSIONS);
Expand Down

0 comments on commit 9ab1ebd

Please sign in to comment.