diff --git a/README.md b/README.md
index 00620f088..2e147efc3 100644
--- a/README.md
+++ b/README.md
@@ -52,10 +52,10 @@ JSQLParser-4.9 is the last JDK8 compatible version and any future development wi
**JSqlParser** aims to support the SQL standard as well as all major RDBMS. Any missing syntax or features can be added on demand.
-| RDBMS | Statements |
-|-----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
-| Oracle
MS SQL Server and Sybase
Postgres
MySQL and MariaDB
DB2
H2 and HSQLDB and Derby
SQLite | `SELECT`
`INSERT`, `UPDATE`, `UPSERT`, `MERGE`
`DELETE`, `TRUNCATE TABLE`
`CREATE ...`, `ALTER ....`, `DROP ...`
`WITH ...` |
-
+| RDBMS | Statements |
+|-----------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
+| Oracle
MS SQL Server and Sybase
Postgres
MySQL and MariaDB
DB2
H2 and HSQLDB and Derby
SQLite | `SELECT`
`INSERT`, `UPDATE`, `UPSERT`, `MERGE`
`DELETE`, `TRUNCATE TABLE`
`CREATE ...`, `ALTER ....`, `DROP ...`
`WITH ...` |
+| Salesforce SOQL | `INCLUDES`, `EXCLUDES` |
**JSqlParser** can also be used to create SQL Statements from Java Code with a fluent API (see [Samples](https://jsqlparser.github.io/JSqlParser/usage.html#build-a-sql-statements)).
diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java
index 426194d25..146285155 100644
--- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java
+++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java
@@ -29,6 +29,7 @@
import net.sf.jsqlparser.expression.operators.relational.Contains;
import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
+import net.sf.jsqlparser.expression.operators.relational.ExcludesExpression;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.FullTextSearch;
@@ -36,6 +37,7 @@
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
+import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
@@ -116,6 +118,10 @@ public interface ExpressionVisitor {
void visit(InExpression inExpression);
+ void visit(IncludesExpression includesExpression);
+
+ void visit(ExcludesExpression excludesExpression);
+
void visit(FullTextSearch fullTextSearch);
void visit(IsNullExpression isNullExpression);
diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java
index ceed08989..657e6d797 100644
--- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java
+++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java
@@ -224,6 +224,18 @@ public void visit(InExpression expr) {
expr.getRightExpression().accept(this);
}
+ @Override
+ public void visit(IncludesExpression expr) {
+ expr.getLeftExpression().accept(this);
+ expr.getRightExpression().accept(this);
+ }
+
+ @Override
+ public void visit(ExcludesExpression expr) {
+ expr.getLeftExpression().accept(this);
+ expr.getRightExpression().accept(this);
+ }
+
@Override
public void visit(IsNullExpression expr) {
expr.getLeftExpression().accept(this);
diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExcludesExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExcludesExpression.java
new file mode 100644
index 000000000..bd9c1de75
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExcludesExpression.java
@@ -0,0 +1,78 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2019 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 ExcludesExpression extends ASTNodeAccessImpl implements Expression {
+
+ private Expression leftExpression;
+ private Expression rightExpression;
+
+ public ExcludesExpression() {}
+
+ public ExcludesExpression(Expression leftExpression, Expression rightExpression) {
+ this.leftExpression = leftExpression;
+ this.rightExpression = rightExpression;
+ }
+
+ public Expression getLeftExpression() {
+ return leftExpression;
+ }
+
+ public ExcludesExpression withLeftExpression(Expression expression) {
+ this.setLeftExpression(expression);
+ return this;
+ }
+
+ public final void setLeftExpression(Expression expression) {
+ leftExpression = expression;
+ }
+
+ public Expression getRightExpression() {
+ return rightExpression;
+ }
+
+ public void setRightExpression(Expression rightExpression) {
+ this.rightExpression = rightExpression;
+ }
+
+ @Override
+ public void accept(ExpressionVisitor expressionVisitor) {
+ expressionVisitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder statementBuilder = new StringBuilder();
+ statementBuilder.append(leftExpression);
+
+ statementBuilder.append(" ");
+ statementBuilder.append("EXCLUDES ");
+
+ statementBuilder.append(rightExpression);
+ return statementBuilder.toString();
+ }
+
+ public ExcludesExpression withRightExpression(Expression rightExpression) {
+ this.setRightExpression(rightExpression);
+ return this;
+ }
+
+ public E getLeftExpression(Class type) {
+ return type.cast(getLeftExpression());
+ }
+
+ public E getRightExpression(Class type) {
+ return type.cast(getRightExpression());
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IncludesExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IncludesExpression.java
new file mode 100644
index 000000000..9bc621d96
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IncludesExpression.java
@@ -0,0 +1,79 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2019 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 IncludesExpression extends ASTNodeAccessImpl
+ implements Expression {
+
+ private Expression leftExpression;
+ private Expression rightExpression;
+
+ public IncludesExpression() {}
+
+ public IncludesExpression(Expression leftExpression, Expression rightExpression) {
+ this.leftExpression = leftExpression;
+ this.rightExpression = rightExpression;
+ }
+
+ public Expression getLeftExpression() {
+ return leftExpression;
+ }
+
+ public IncludesExpression withLeftExpression(Expression expression) {
+ this.setLeftExpression(expression);
+ return this;
+ }
+
+ public final void setLeftExpression(Expression expression) {
+ leftExpression = expression;
+ }
+
+ public Expression getRightExpression() {
+ return rightExpression;
+ }
+
+ public void setRightExpression(Expression rightExpression) {
+ this.rightExpression = rightExpression;
+ }
+
+ @Override
+ public void accept(ExpressionVisitor expressionVisitor) {
+ expressionVisitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder statementBuilder = new StringBuilder();
+ statementBuilder.append(leftExpression);
+
+ statementBuilder.append(" ");
+ statementBuilder.append("INCLUDES ");
+
+ statementBuilder.append(rightExpression);
+ return statementBuilder.toString();
+ }
+
+ public IncludesExpression withRightExpression(Expression rightExpression) {
+ this.setRightExpression(rightExpression);
+ return this;
+ }
+
+ public E getLeftExpression(Class type) {
+ return type.cast(getLeftExpression());
+ }
+
+ public E getRightExpression(Class type) {
+ return type.cast(getRightExpression());
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java
index 10b59b488..5bce65ccc 100644
--- a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java
+++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java
@@ -64,6 +64,7 @@ public class ParserKeywordsUtils {
{"DOUBLE", RESTRICTED_ALIAS},
{"ELSE", RESTRICTED_JSQLPARSER},
{"EXCEPT", RESTRICTED_SQL2016},
+ {"EXCLUDES", RESTRICTED_JSQLPARSER},
{"EXISTS", RESTRICTED_SQL2016},
{"FETCH", RESTRICTED_SQL2016},
{"FINAL", RESTRICTED_JSQLPARSER},
@@ -82,6 +83,7 @@ public class ParserKeywordsUtils {
{"IGNORE", RESTRICTED_ALIAS},
{"ILIKE", RESTRICTED_SQL2016},
{"IN", RESTRICTED_SQL2016},
+ {"INCLUDES", RESTRICTED_JSQLPARSER},
{"INNER", RESTRICTED_SQL2016},
{"INTERSECT", RESTRICTED_SQL2016},
{"INTERVAL", RESTRICTED_SQL2016},
diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
index f7a9a5b53..ea288f37f 100644
--- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
+++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
@@ -88,6 +88,7 @@
import net.sf.jsqlparser.expression.operators.relational.Contains;
import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
+import net.sf.jsqlparser.expression.operators.relational.ExcludesExpression;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.FullTextSearch;
@@ -95,6 +96,7 @@
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
+import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
@@ -418,6 +420,18 @@ public void visit(InExpression inExpression) {
inExpression.getRightExpression().accept(this);
}
+ @Override
+ public void visit(IncludesExpression includesExpression) {
+ includesExpression.getLeftExpression().accept(this);
+ includesExpression.getRightExpression().accept(this);
+ }
+
+ @Override
+ public void visit(ExcludesExpression excludesExpression) {
+ excludesExpression.getLeftExpression().accept(this);
+ excludesExpression.getRightExpression().accept(this);
+ }
+
@Override
public void visit(FullTextSearch fullTextSearch) {
diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java
index 0439b397f..13f543b4d 100644
--- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java
@@ -83,6 +83,7 @@
import net.sf.jsqlparser.expression.operators.relational.Contains;
import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
+import net.sf.jsqlparser.expression.operators.relational.ExcludesExpression;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.FullTextSearch;
@@ -90,6 +91,7 @@
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
+import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
@@ -266,6 +268,20 @@ public void visit(InExpression inExpression) {
inExpression.getRightExpression().accept(this);
}
+ @Override
+ public void visit(IncludesExpression includesExpression) {
+ includesExpression.getLeftExpression().accept(this);
+ buffer.append(" INCLUDES ");
+ includesExpression.getRightExpression().accept(this);
+ }
+
+ @Override
+ public void visit(ExcludesExpression excludesExpression) {
+ excludesExpression.getLeftExpression().accept(this);
+ buffer.append(" EXCLUDES ");
+ excludesExpression.getRightExpression().accept(this);
+ }
+
@Override
public void visit(FullTextSearch fullTextSearch) {
// Build a list of matched columns
diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java
index f3c4de330..8bd19537b 100644
--- a/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java
+++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java
@@ -84,6 +84,7 @@
import net.sf.jsqlparser.expression.operators.relational.Contains;
import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
+import net.sf.jsqlparser.expression.operators.relational.ExcludesExpression;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.FullTextSearch;
@@ -91,6 +92,7 @@
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
+import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
@@ -225,6 +227,18 @@ public void visit(InExpression inExpression) {
validateOptionalExpression(inExpression.getRightExpression(), this);
}
+ @Override
+ public void visit(IncludesExpression includesExpression) {
+ validateOptionalExpression(includesExpression.getLeftExpression(), this);
+ validateOptionalExpression(includesExpression.getRightExpression(), this);
+ }
+
+ @Override
+ public void visit(ExcludesExpression excludesExpression) {
+ validateOptionalExpression(excludesExpression.getLeftExpression(), this);
+ validateOptionalExpression(excludesExpression.getRightExpression(), this);
+ }
+
@Override
public void visit(FullTextSearch fullTextSearch) {
validateOptionalExpressions(fullTextSearch.getMatchColumns());
diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
index ad4b568dd..d55a2228e 100644
--- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
+++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
@@ -224,6 +224,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
|
|
|
+| /* Salesforce SOQL */
|
|
|
@@ -264,6 +265,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
|
|
|
+| /* Salesforce SOQL */
|
|
|
@@ -3609,6 +3611,10 @@ Expression SQLCondition():
[
LOOKAHEAD(2) (
(
+ LOOKAHEAD(ExcludesExpression()) result=ExcludesExpression(left)
+ |
+ LOOKAHEAD(IncludesExpression()) result=IncludesExpression(left)
+ |
LOOKAHEAD(2) result=Between(left)
|
result = MemberOfExpression(left)
@@ -3662,6 +3668,36 @@ Expression InExpression() #InExpression :
}
}
+Expression IncludesExpression(Expression leftExpression) #IncludesExpression :
+{
+ Token token;
+ Expression rightExpression;
+}
+{
+ (rightExpression = ParenthesedExpressionList())
+ {
+ IncludesExpression includesExpression = new IncludesExpression(leftExpression, rightExpression);
+
+ linkAST(includesExpression,jjtThis);
+ return includesExpression;
+ }
+}
+
+Expression ExcludesExpression(Expression leftExpression) #ExcludesExpression :
+{
+ Token token;
+ Expression rightExpression;
+}
+{
+ (rightExpression = ParenthesedExpressionList())
+ {
+ ExcludesExpression excludesExpression = new ExcludesExpression(leftExpression, rightExpression);
+
+ linkAST(excludesExpression,jjtThis);
+ return excludesExpression;
+ }
+}
+
Expression Between(Expression leftExpression) :
{
Between result = new Between();
diff --git a/src/site/sphinx/keywords.rst b/src/site/sphinx/keywords.rst
index 950e8efe1..91437ece5 100644
--- a/src/site/sphinx/keywords.rst
+++ b/src/site/sphinx/keywords.rst
@@ -45,6 +45,8 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and
+----------------------+-------------+-----------+
| EXCEPT | Yes | Yes |
+----------------------+-------------+-----------+
+| EXCLUDES | Yes | Yes |
++----------------------+-------------+-----------+
| EXISTS | Yes | Yes |
+----------------------+-------------+-----------+
| FETCH | Yes | Yes |
@@ -81,6 +83,8 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and
+----------------------+-------------+-----------+
| IN | Yes | Yes |
+----------------------+-------------+-----------+
+| INCLUDES | Yes | Yes |
++----------------------+-------------+-----------+
| INNER | Yes | Yes |
+----------------------+-------------+-----------+
| INTERSECT | Yes | Yes |
diff --git a/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java b/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java
index 14ae67fed..f7ad3994f 100644
--- a/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java
+++ b/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java
@@ -11,8 +11,11 @@
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.operators.conditional.XorExpression;
+import net.sf.jsqlparser.expression.operators.relational.ExcludesExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
+import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
+import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.AllTableColumns;
@@ -268,4 +271,44 @@ public void testAnalyticExpressionWithPartialWindowElement() throws JSQLParserEx
expression.accept(adapter);
}
+
+ @Test
+ public void testIncludesExpression() throws JSQLParserException {
+ final List