diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java index c9e91b18c..19e1ad471 100644 --- a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java +++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java @@ -121,6 +121,7 @@ public class ParserKeywordsUtils { {"OVERWRITE ", RESTRICTED_JSQLPARSER}, {"PIVOT", RESTRICTED_JSQLPARSER}, {"PREFERRING", RESTRICTED_JSQLPARSER}, + {"PREWHERE", RESTRICTED_JSQLPARSER}, {"PRIOR", RESTRICTED_ALIAS}, {"PROCEDURE", RESTRICTED_ALIAS}, {"PUBLIC", RESTRICTED_ALIAS}, diff --git a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java index 8698e3152..87bae64eb 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java @@ -37,6 +37,7 @@ public class PlainSelect extends Select { private FromItem fromItem; private List lateralViews; private List joins; + private Expression preWhere; private Expression where; private GroupByElement groupBy; private Expression having; @@ -160,6 +161,14 @@ public void setWhere(Expression where) { this.where = where; } + public Expression getPreWhere() { + return preWhere; + } + + public void setPreWhere(Expression preWhere) { + this.preWhere = preWhere; + } + public PlainSelect withFromItem(FromItem item) { this.setFromItem(item); return this; @@ -569,6 +578,9 @@ public StringBuilder appendSelectBodyTo(StringBuilder builder) { if (ksqlWindow != null) { builder.append(" WINDOW ").append(ksqlWindow); } + if (preWhere != null) { + builder.append(" PREWHERE ").append(preWhere); + } if (where != null) { builder.append(" WHERE ").append(where); } @@ -597,6 +609,9 @@ public StringBuilder appendSelectBodyTo(StringBuilder builder) { } } else { // without from + if (preWhere != null) { + builder.append(" PREWHERE ").append(preWhere); + } if (where != null) { builder.append(" WHERE ").append(where); } @@ -669,6 +684,11 @@ public PlainSelect withWhere(Expression where) { return this; } + public PlainSelect withPreWhere(Expression preWhere) { + this.setPreWhere(preWhere); + return this; + } + public PlainSelect withOptimizeFor(OptimizeFor optimizeFor) { this.setOptimizeFor(optimizeFor); return this; @@ -767,6 +787,10 @@ public E getWhere(Class type) { return type.cast(getWhere()); } + public E getPreWhere(Class type) { + return type.cast(getPreWhere()); + } + public E getHaving(Class type) { return type.cast(getHaving()); } diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java index aa0052c15..f968f9015 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java @@ -151,6 +151,7 @@ public T visit(PlainSelect plainSelect, S context) { // //@todo: implement // } + expressionVisitor.visitExpression(plainSelect.getPreWhere(), context); expressionVisitor.visitExpression(plainSelect.getWhere(), context); // if (plainSelect.getOracleHierarchical() != null) { diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index f61c02e70..d0f7a508a 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -317,6 +317,9 @@ public Void visit(PlainSelect plainSelect, S context) { } visitJoins(plainSelect.getJoins(), context); + if (plainSelect.getPreWhere() != null) { + plainSelect.getPreWhere().accept(this, context); + } if (plainSelect.getWhere() != null) { plainSelect.getWhere().accept(this, context); } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java index 5059f7cea..7b03e66a0 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -287,6 +287,7 @@ public StringBuilder visit(PlainSelect plainSelect, S context) { builder.append(plainSelect.getKsqlWindow().toString()); } + deparsePreWhereClause(plainSelect); deparseWhereClause(plainSelect); if (plainSelect.getOracleHierarchical() != null) { @@ -394,6 +395,13 @@ protected void deparseWhereClause(PlainSelect plainSelect) { } } + protected void deparsePreWhereClause(PlainSelect plainSelect) { + if (plainSelect.getPreWhere() != null) { + builder.append(" PREWHERE "); + plainSelect.getPreWhere().accept(expressionVisitor, null); + } + } + protected void deparseDistinctClause(Distinct distinct) { if (distinct != null) { if (distinct.isUseUnique()) { diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java index bacd4e1af..36741ecd6 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java @@ -119,6 +119,7 @@ public Void visit(PlainSelect plainSelect, S context) { // validateOptionalList(plainSelect.getSelectItems(), () -> this, SelectItem::accept, // context); + validateOptionalExpression(plainSelect.getPreWhere()); validateOptionalExpression(plainSelect.getWhere()); validateOptionalExpression(plainSelect.getOracleHierarchical()); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index e6b70cae6..efab9d8fd 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -539,6 +539,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -4118,6 +4119,7 @@ PlainSelect PlainSelect() #PlainSelect: List lateralViews = null; List joins = null; List> distinctOn = null; + Expression preWhere = null; Expression where = null; ForClause forClause = null; List orderByElements; @@ -4213,6 +4215,7 @@ PlainSelect PlainSelect() #PlainSelect: [ LOOKAHEAD(2) { plainSelect.setUsingFinal(true); } ] [ LOOKAHEAD(2) ksqlWindow=KSQLWindowClause() { plainSelect.setKsqlWindow(ksqlWindow); } ] + [ LOOKAHEAD(2) preWhere=PreWhereClause() { plainSelect.setPreWhere(preWhere); }] [ LOOKAHEAD(2) where=WhereClause() { plainSelect.setWhere(where); }] [ LOOKAHEAD(2) oracleHierarchicalQueryClause=OracleHierarchicalQueryClause() { plainSelect.setOracleHierarchical(oracleHierarchicalQueryClause); } ] [ LOOKAHEAD(2) preferringClause=PreferringClause() { plainSelect.setPreferringClause(preferringClause); } @@ -5088,6 +5091,15 @@ Expression WhereClause(): { return retval; } } +Expression PreWhereClause(): +{ + Expression retval = null; +} +{ + retval=Expression() + { return retval; } +} + OracleHierarchicalExpression OracleHierarchicalQueryClause(): { OracleHierarchicalExpression result = new OracleHierarchicalExpression(); diff --git a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java index 72b8508df..3c9e99f97 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java @@ -59,4 +59,21 @@ public void execute() throws Throwable { } }, "Fail when restricted keyword GLOBAL is used as an Alias."); } + + @Test + public void testPreWhereClause() throws JSQLParserException { + String sqlStr = "SELECT * FROM table1 PREWHERE column_name = 'value'"; + PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(sqlStr, true); + Assertions.assertNotNull(select.getPreWhere()); + Assertions.assertNull(select.getWhere()); + } + + @Test + public void testPreWhereWithWhereClause() throws JSQLParserException { + String sqlStr = + "SELECT * FROM table1 PREWHERE column_name = 'value' WHERE id > 10"; + PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(sqlStr, true); + Assertions.assertNotNull(select.getPreWhere()); + Assertions.assertNotNull(select.getWhere()); + } } diff --git a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java index dd7b93c7f..ff629a2e8 100644 --- a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java +++ b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java @@ -55,6 +55,14 @@ public void testGetTablesWithXor() throws Exception { assertThat(TablesNamesFinder.findTables(sqlStr)).containsExactlyInAnyOrder("MY_TABLE1"); } + @Test + public void testGetTablesWithPreWhere() throws Exception { + String sqlStr = + "SELECT * FROM MY_TABLE1 PREWHERE ID IN (SELECT ID FROM MY_TABLE2)"; + assertThat(TablesNamesFinder.findTables(sqlStr)).containsExactlyInAnyOrder("MY_TABLE1", + "MY_TABLE2"); + } + @Test public void testGetTablesWithStmt() throws Exception { String sqlStr = @@ -734,4 +742,3 @@ void testNestedTablesInJsonObject() throws JSQLParserException { "table2", "table3"); } } -