From 53d2fa2c1d59ab62cf635cae6d8052fdd02997c5 Mon Sep 17 00:00:00 2001 From: MathewJoseph31 Date: Tue, 22 Nov 2016 21:31:26 +0530 Subject: [PATCH 1/3] Support for postgres overlap operator && added, natural left/right/full outer joins added --- .../expression/ExpressionVisitor.java | 3 ++ .../expression/ExpressionVisitorAdapter.java | 6 +++ .../operators/relational/DoubleAnd.java | 41 +++++++++++++++++++ .../sf/jsqlparser/util/TablesNamesFinder.java | 8 ++++ .../util/deparser/ExpressionDeParser.java | 7 ++++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 6 ++- .../sf/jsqlparser/test/select/SelectTest.java | 5 +++ 7 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index 864168db5..e896299fe 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -44,6 +44,7 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThan; import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;//Added by mathew on 21st Nov 2016 import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; import net.sf.jsqlparser.expression.operators.relational.JsonOperator; @@ -109,6 +110,8 @@ public interface ExpressionVisitor { void visit(MinorThanEquals minorThanEquals); void visit(NotEqualsTo notEqualsTo); + + void visit(DoubleAnd doubleAnd);//Added by mathew on 21st Nov 2016 void visit(Column tableColumn); diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index 974deef35..c176a5e84 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -204,6 +204,12 @@ public void visit(MinorThanEquals expr) { public void visit(NotEqualsTo expr) { visitBinaryExpression(expr); } + + /*Added by mathew on 21st Nov 2016*/ + @Override + public void visit(DoubleAnd expr) { + visitBinaryExpression(expr); + } @Override public void visit(Column column) { diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java new file mode 100644 index 000000000..8b531cafd --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java @@ -0,0 +1,41 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +/*Added by Mathew on 1st Aug 2016*/ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class DoubleAnd extends ComparisonOperator { + + public DoubleAnd() { + super("&&"); + } + + public DoubleAnd(String operator) { + super(operator); + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 7672b8632..b79d59d14 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -258,6 +258,14 @@ public void visit(Multiplication multiplication) { public void visit(NotEqualsTo notEqualsTo) { visitBinaryExpression(notEqualsTo); } + + /* Added by Mathew on 21st Nov 2016 + * + */ + @Override + public void visit(DoubleAnd doubleAnd) { + visitBinaryExpression(doubleAnd); + } @Override public void visit(NullValue nullValue) { 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 422660d05..79dd430aa 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -235,6 +235,13 @@ public void visit(NotEqualsTo notEqualsTo) { visitOldOracleJoinBinaryExpression(notEqualsTo, " " + notEqualsTo.getStringExpression() + " "); } + + /*Added by Mathew on November 21st 2016*/ + @Override + public void visit(DoubleAnd doubleAnd) { + visitOldOracleJoinBinaryExpression(doubleAnd, " " + doubleAnd.getStringExpression() + " "); + + } @Override public void visit(NullValue nullValue) { diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index e8b879011..569ec4b41 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -266,6 +266,7 @@ TOKEN : /* Operators */ | )* "="> | )* ">"> | /*added by mathew on 21st Nov 2016*/ } TOKEN : /* Date/Time with time zones */ @@ -1333,7 +1334,9 @@ Join JoinerExpression(): | { join.setFull(true); } ) [ { join.setOuter(true); } ] | { join.setInner(true); } - | { join.setNatural(true); } + | { join.setNatural(true); } + [( { join.setLeft(true); } | { join.setRight(true); }| { join.setFull(true); }) { join.setOuter(true); } ] + /* line above added by mathew on 21 Nov 2016*/ | { join.setCross(true); } ] @@ -1723,6 +1726,7 @@ Expression RegularCondition(): | token= { result = new MinorThanEquals(token.image); } | token= { result = new NotEqualsTo(token.image); } | token= { result = new NotEqualsTo(token.image); } + | token= { result = new DoubleAnd(token.image); } | "@@" { result = new Matches(); } | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } | [ { binary=true; } ] { result = new RegExpMySQLOperator(binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } diff --git a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java index f1ab1497c..d161c1826 100644 --- a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java @@ -1847,6 +1847,11 @@ public void testNotEqualsTo() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a != b"); assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a <> b"); } + + /*Added by Mathew on 21st Nov 2016*/ + public void testDoubleAnd() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a && b"); + } public void testJsonExpression() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT data->'images'->'thumbnail'->'url' AS thumb FROM instagram"); From cf7f53df41331c900fb0e6ceccfacca26218fd62 Mon Sep 17 00:00:00 2001 From: MathewJoseph31 Date: Tue, 22 Nov 2016 23:41:31 +0530 Subject: [PATCH 2/3] Support for Array Contains (&>) and ContainedBy (<&) operator added --- .../expression/ExpressionVisitor.java | 6 +++ .../expression/ExpressionVisitorAdapter.java | 13 ++++++ .../operators/relational/ContainedBy.java | 42 +++++++++++++++++++ .../operators/relational/Contains.java | 42 +++++++++++++++++++ .../sf/jsqlparser/util/TablesNamesFinder.java | 16 +++++++ .../util/deparser/ExpressionDeParser.java | 14 +++++++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 4 ++ .../sf/jsqlparser/test/select/SelectTest.java | 10 +++++ 8 files changed, 147 insertions(+) create mode 100644 src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java create mode 100644 src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index e896299fe..6eef5eeac 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -45,6 +45,8 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;//Added by mathew on 21st Nov 2016 +import net.sf.jsqlparser.expression.operators.relational.Contains;//Added by mathew on 21st Nov 2016 +import net.sf.jsqlparser.expression.operators.relational.ContainedBy;//Added by mathew on 21st Nov 2016 import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; import net.sf.jsqlparser.expression.operators.relational.JsonOperator; @@ -112,6 +114,10 @@ public interface ExpressionVisitor { void visit(NotEqualsTo notEqualsTo); void visit(DoubleAnd doubleAnd);//Added by mathew on 21st Nov 2016 + + void visit(Contains contains);//Added by mathew on 21st Nov 2016 + + void visit(ContainedBy containedBy);//Added by mathew on 21st Nov 2016 void visit(Column tableColumn); diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index c176a5e84..af00daf2d 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -210,6 +210,19 @@ public void visit(NotEqualsTo expr) { public void visit(DoubleAnd expr) { visitBinaryExpression(expr); } + + /*Added by mathew on 21st Nov 2016*/ + @Override + public void visit(Contains expr) { + visitBinaryExpression(expr); + } + + /*Added by mathew on 21st Nov 2016*/ + @Override + public void visit(ContainedBy expr) { + visitBinaryExpression(expr); + } + @Override public void visit(Column column) { diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java new file mode 100644 index 000000000..ab154f18f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java @@ -0,0 +1,42 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +/*Added by Mathew on 21st Nov 2016*/ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class ContainedBy extends ComparisonOperator { + + public ContainedBy() { + super("<&"); + } + + public ContainedBy(String operator) { + super(operator); + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } +} + diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java new file mode 100644 index 000000000..98dcac02d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java @@ -0,0 +1,42 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +/*Added by Mathew on 21st Nov 2016*/ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Contains extends ComparisonOperator { + + public Contains() { + super("&>"); + } + + public Contains(String operator) { + super(operator); + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } +} + diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index b79d59d14..c2038cf67 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -266,6 +266,22 @@ public void visit(NotEqualsTo notEqualsTo) { public void visit(DoubleAnd doubleAnd) { visitBinaryExpression(doubleAnd); } + + /* Added by Mathew on 21st Nov 2016 + * + */ + @Override + public void visit(Contains contains) { + visitBinaryExpression(contains); + } + + /* Added by Mathew on 21st Nov 2016 + * + */ + @Override + public void visit(ContainedBy containedBy) { + visitBinaryExpression(containedBy); + } @Override public void visit(NullValue nullValue) { 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 79dd430aa..cded9145d 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -242,6 +242,20 @@ public void visit(DoubleAnd doubleAnd) { visitOldOracleJoinBinaryExpression(doubleAnd, " " + doubleAnd.getStringExpression() + " "); } + + /*Added by Mathew on November 21st 2016*/ + @Override + public void visit(Contains contains) { + visitOldOracleJoinBinaryExpression(contains, " " + contains.getStringExpression() + " "); + + } + + /*Added by Mathew on November 21st 2016*/ + @Override + public void visit(ContainedBy containedBy) { + visitOldOracleJoinBinaryExpression(containedBy, " " + containedBy.getStringExpression() + " "); + + } @Override public void visit(NullValue nullValue) { diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 569ec4b41..6abfcb3a6 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -267,6 +267,8 @@ TOKEN : /* Operators */ | )* ">"> | /*added by mathew on 21st Nov 2016*/ +| "> /*added by mathew on 21st Nov 2016*/ +| /*added by mathew on 21st Nov 2016*/ } TOKEN : /* Date/Time with time zones */ @@ -1727,6 +1729,8 @@ Expression RegularCondition(): | token= { result = new NotEqualsTo(token.image); } | token= { result = new NotEqualsTo(token.image); } | token= { result = new DoubleAnd(token.image); } + | token= { result = new DoubleAnd(token.image); } + | token= { result = new DoubleAnd(token.image); } | "@@" { result = new Matches(); } | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } | [ { binary=true; } ] { result = new RegExpMySQLOperator(binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } diff --git a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java index d161c1826..6b63cb006 100644 --- a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java @@ -1852,6 +1852,16 @@ public void testNotEqualsTo() throws JSQLParserException { public void testDoubleAnd() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a && b"); } + + /*Added by Mathew on 21st Nov 2016*/ + public void testContains() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a &> b"); + } + + /*Added by Mathew on 21st Nov 2016*/ + public void testContainedBy() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a <& b"); + } public void testJsonExpression() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT data->'images'->'thumbnail'->'url' AS thumb FROM instagram"); From aafc2e39d750f355ba289a9dc7a5b06da197c672 Mon Sep 17 00:00:00 2001 From: MathewJoseph31 Date: Wed, 23 Nov 2016 15:24:02 +0530 Subject: [PATCH 3/3] Support for Nested With Clauses Added --- .../jsqlparser/util/WithTransformation.java | 415 ++++++++++++++++++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 13 +- 2 files changed, 426 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/util/WithTransformation.java diff --git a/src/main/java/net/sf/jsqlparser/util/WithTransformation.java b/src/main/java/net/sf/jsqlparser/util/WithTransformation.java new file mode 100644 index 000000000..deeb24054 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/WithTransformation.java @@ -0,0 +1,415 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2016 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * @author mathew joseph (MathewJoseph31) + * + */ +package net.sf.jsqlparser.util; + +import java.util.List; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.AllComparisonExpression; +import net.sf.jsqlparser.expression.AnyComparisonExpression; +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; +import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.statement.select.FromItem; +import net.sf.jsqlparser.statement.select.Join; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.SelectBody; +import net.sf.jsqlparser.statement.select.SelectExpressionItem; +import net.sf.jsqlparser.statement.select.SelectItem; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.SubJoin; +import net.sf.jsqlparser.statement.select.SubSelect; +import net.sf.jsqlparser.statement.select.WithItem; + +public class WithTransformation { + SelectBody selectBody; + java.util.logging.Logger logger=java.util.logging.Logger.getLogger(WithTransformation.class.getName()); + + public WithTransformation(SelectBody selBody,List withItemsList) { + selectBody=selBody; + if(withItemsList==null||withItemsList.isEmpty()) + return; + for(int i=0;i), and Join ( in the above case ), deal with them separately + */ + + SubJoin transformSubJoinForWithAs(WithItem srcWithItem, SubJoin subJoin){ + FromItem leftFromItem=subJoin.getLeft(); + //checks the left item (assumed to be a Table), if its name is the same as the name of the + // input with item, the name is substitued by its corresponding definition + if(leftFromItem instanceof net.sf.jsqlparser.schema.Table){ + net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)leftFromItem; + if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ + logger.info("found as from item"); + //Get withItem and create new SubSelect if fromItem name is equal to with item name + Alias a; + if(tarTable.getAlias()!=null) + a=new Alias(tarTable.getAlias().getName()); + else + a = new Alias(srcWithItem.getName()); + a.setUseAs(true); + SubSelect sub = new SubSelect(); + sub.setSelectBody(srcWithItem.getSelectBody()); + sub.setAlias(a); + subJoin.setLeft(sub); + } + } + //checks if the left item (assumed to be a Table or SubJoin itself) is a SubJoin, if then recursive call + else if(leftFromItem instanceof SubJoin){ + transformSubJoinForWithAs(srcWithItem,(SubJoin)leftFromItem); + } + // if fromitem is a subselect , then call the corresponding method that handles it + else if(leftFromItem instanceof SubSelect){ + logger.info("processing subselect"); + SubSelect leftSubSelect=(SubSelect) leftFromItem; + transformSubSelectForWithAs(srcWithItem, leftSubSelect); + } + // deals with the right item of the subjoin + FromItem rightFromItem=subJoin.getJoin().getRightItem(); + //checks if the right item (assumed to be a Table or SubJoin itself) is a Table, then if its name is the same as the name of the + // input with item, the name is substituted by its corresponding definition + + if(rightFromItem instanceof net.sf.jsqlparser.schema.Table){ + net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)rightFromItem; + if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ + logger.info("found as from item"); + //Get withItem and create new SubSelect if fromItem name is equal to with item name + Alias a; + if(tarTable.getAlias()!=null) + a=new Alias(tarTable.getAlias().getName()); + else + a = new Alias(srcWithItem.getName()); + a.setUseAs(true); + SubSelect sub = new SubSelect(); + sub.setSelectBody(srcWithItem.getSelectBody()); + sub.setAlias(a); + subJoin.getJoin().setRightItem(sub); + } + } + //checks if the right item (assumed to be a Table or SubJoin itself) is a SubJoin, if then recursive call + else if(rightFromItem instanceof SubJoin){ + transformSubJoinForWithAs(srcWithItem,(SubJoin)rightFromItem); + } + else if(rightFromItem instanceof SubSelect){ + logger.info("processing subselect"); + SubSelect rightSubSelect=(SubSelect) rightFromItem; + transformSubSelectForWithAs(srcWithItem, rightSubSelect); + } + return subJoin; + } + + /** @author mathew + * Transforms the given input PlainSelect statement by substituting alias name of its first argument + * with its corresponding definition in its select body + * + */ + + PlainSelect transformPlainSelectForWithAs(WithItem srcWithItem, PlainSelect tarSelectClause){ + // Starts by dealing with from items, a from item can be a Table name, a subselect statement, or a subjoin statement + FromItem tarFromItem=tarSelectClause.getFromItem(); + // if fromitem is a Table then check if its name is equal to the name of the input withitem, if yes substitutes its occurence + // by its definition + if(tarFromItem instanceof net.sf.jsqlparser.schema.Table){ + net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)tarFromItem; + if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ + logger.info("found as from item"); + //Get withItem and create new SubSelect if fromItem name is equal to with item name + Alias a; + if(tarTable.getAlias()!=null) + a=new Alias(tarTable.getAlias().getName()); + else + a = new Alias(srcWithItem.getName()); + a.setUseAs(true); + SubSelect sub = new SubSelect(); + sub.setSelectBody(srcWithItem.getSelectBody()); + sub.setAlias(a); + tarSelectClause.setFromItem(sub); + } + } + // if fromitem is a subselect , then call the corresponding method that handles it + else if(tarFromItem instanceof SubSelect){ + logger.info("processing subselect"); + SubSelect tarSubSelect=(SubSelect) tarFromItem; + transformSubSelectForWithAs(srcWithItem, tarSubSelect); + } + // if fromitem is a subjoin, then call the corresponding method that handles it + else if(tarFromItem instanceof SubJoin){ + logger.info("processing subjoin"); + SubJoin tarSubJoin=(SubJoin)tarFromItem; + tarSubJoin=transformSubJoinForWithAs(srcWithItem,tarSubJoin); + } + + // WITH AS names can be in joins as well, call the corresponding method that handles it + if(tarSelectClause.getJoins() != null ){ + logger.info("processing joins"); + List joinList = tarSelectClause.getJoins(); + transformJoinsForWithAs(srcWithItem, joinList); + + } + // Now deal with its where clauses + transformWhereClauseForWithAs(srcWithItem, tarSelectClause.getWhere()); + return tarSelectClause; + } + + /** @author mathew + * Extract the select statement from the subselect. It can be either a PlainSelect + * or a SetOperation, deal with their transformations separately + */ + + private void transformSubSelectForWithAs(WithItem srcWithItem, SubSelect tarSubSelect) { + //when the select body is a plain select statement, then call the corresponding method that handles it + if(tarSubSelect.getSelectBody() instanceof PlainSelect){ + PlainSelect tempSelect=(PlainSelect)tarSubSelect.getSelectBody(); + transformPlainSelectForWithAs(srcWithItem, tempSelect); + } + //where the select body is a set operation (union, intersect etc), then call the corresponding method that handles it + else if(tarSubSelect.getSelectBody() instanceof SetOperationList){ + SetOperationList setOpList=(SetOperationList)tarSubSelect.getSelectBody(); + transformSetOperationListForWithAs(srcWithItem, setOpList); + } + + } + + /** @author mathew + * Transforms set operation statements eg: (A Union (B INTERSECT C) + * Splits the operands and deals with them separately + */ + + private void transformSetOperationListForWithAs(WithItem srcWithItem, SetOperationList setOpList) { + // TODO Auto-generated method stub + for(SelectBody selBody:setOpList.getSelects()){ + if(selBody instanceof PlainSelect){ + PlainSelect tarSelectClause=(PlainSelect) selBody; + transformPlainSelectForWithAs(srcWithItem, tarSelectClause); + } + else if(selBody instanceof SetOperationList){ + transformSetOperationListForWithAs(srcWithItem, (SetOperationList)selBody); + } + } + + } + + /** @author mathew + * Transforms joinList in the from clause of a select statement by looking for occurences + * of the name of the input with item, if any occurence is found its corresponding definition is substituted as a subselect + * Each join item is assumed to be a table, a subselect, or a subjoin + */ + private void transformJoinsForWithAs(WithItem srcWithItem, List joinList){ + for(int k=0; k < joinList.size(); k++){ + Join jcl = joinList.get(k); + FromItem tarJoinFromItem=jcl.getRightItem(); + //if the join item is a table, then call the corresponding method that handles it + if(tarJoinFromItem instanceof net.sf.jsqlparser.schema.Table){ + net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)tarJoinFromItem; + if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ + logger.info("found with alias in from item of join"); + //Get withItem and create new SubSelect if fromItem name is equal to with item name + Alias a; + if(tarTable.getAlias()!=null) + a=new Alias(tarTable.getAlias().getName()); + else + a = new Alias(srcWithItem.getName()); + a.setUseAs(true); + SubSelect sub = new SubSelect(); + sub.setSelectBody(srcWithItem.getSelectBody()); + sub.setAlias(a); + jcl.setRightItem(sub); + } + } + //if join item is a subselect, then call the corresponding method that handles it + else if(tarJoinFromItem instanceof SubSelect){ + logger.info("processing subselect in join"); + SubSelect tarSubSelect=(SubSelect) tarJoinFromItem; + PlainSelect tempSelect=(PlainSelect)tarSubSelect.getSelectBody(); + transformPlainSelectForWithAs(srcWithItem, tempSelect); + } + //if join item is a subjoin, then call the corresponding method that handles it + else if(tarJoinFromItem instanceof SubJoin){ + logger.info("processing subjoin in join"); + SubJoin tarSubJoin=(SubJoin)tarJoinFromItem; + tarSubJoin=transformSubJoinForWithAs(srcWithItem,tarSubJoin); + } + + } + } + + /** @author mathew + * Transforms the expression in the where clause of a select statement by looking for occurences + * of the name of the input with item, if any occurence is found its corresponding definition is substituted as a subselect + */ + private void transformWhereClauseForWithAs(WithItem srcWithItem, Expression whereClause) { + //if whereClause is a subselect, then call the corresponding method that handles it + if(whereClause instanceof SubSelect){ + SubSelect subSelect=(SubSelect) whereClause; + transformPlainSelectForWithAs(srcWithItem, (PlainSelect)(subSelect.getSelectBody())); + } + //if whereClause is a BinaryExpression (AND, OR etc.), then + //split its operands and recursive calls using them as arguments + if(whereClause instanceof BinaryExpression){ + BinaryExpression binExpression = ((BinaryExpression) whereClause); + transformWhereClauseForWithAs(srcWithItem, binExpression.getLeftExpression()); + transformWhereClauseForWithAs(srcWithItem, binExpression.getRightExpression()); + } + //if whereClause is a Exists Expression, then recurisively call using its right expression as argument + else if(whereClause instanceof ExistsExpression){ + logger.info("transforming exists in where clause"); + ExistsExpression existsExpression = (ExistsExpression)whereClause; + transformWhereClauseForWithAs(srcWithItem,existsExpression.getRightExpression()); + } + //if whereClause is a InExpression, then handle its left and right operands separately + else if(whereClause instanceof InExpression){ + logger.info("transforming inExpression in where clause"); + InExpression inExpression = (InExpression)whereClause; + if (inExpression.getLeftItemsList() instanceof SubSelect){ + transformWhereClauseForWithAs(srcWithItem, (SubSelect)inExpression.getLeftItemsList()); + } + if(inExpression.getRightItemsList() instanceof SubSelect){ + transformWhereClauseForWithAs(srcWithItem, (SubSelect)inExpression.getRightItemsList()); + } + } + //if whereClause is a All comparision Expression, then recurisively call using its subselect as argument + else if(whereClause instanceof AllComparisonExpression){ + logger.info("transforming all comparison in where clause"); + AllComparisonExpression ace = (AllComparisonExpression) whereClause; + transformPlainSelectForWithAs(srcWithItem, (PlainSelect)(ace.getSubSelect().getSelectBody())); + } + //if whereClause is a Any comparision Expression, then recurisively call using its subselect as argument + else if(whereClause instanceof AnyComparisonExpression){ + logger.info("transforming any comparison in where clause"); + AnyComparisonExpression ace = (AnyComparisonExpression) whereClause; + transformPlainSelectForWithAs(srcWithItem, (PlainSelect)(ace.getSubSelect().getSelectBody())); + } + + } + + + + /** @author mathew + * normalize column names in with items eg: With A(a) as (select name from ...) + * is normalized to With A(a) as (select name as a from ...) + */ + private WithItem normalizeWithItem(WithItem withItem) { + // TODO Auto-generated method stub + + if(withItem.getSelectBody() instanceof PlainSelect){ + PlainSelect selectClause =(PlainSelect) withItem.getSelectBody(); + normalizeSelectedColumnsForWithItem(withItem, selectClause); + + } + if(withItem.getSelectBody() instanceof SetOperationList){ + SetOperationList setOpList=(SetOperationList)withItem.getSelectBody(); + normalizeSelectedColumnsForWithItem(withItem, setOpList); + + } + logger.info(" normalized with item "+withItem.getName()+" withItemBody: "+withItem.getSelectBody()); + return withItem; + } + + + /** @author mathew + * deals with normalization of Setoperation Queries. Eg. A, B, C are normalized individually + * in a select query of the form A UNION B UNION C + */ + private void normalizeSelectedColumnsForWithItem(WithItem withItem, SetOperationList setOpList) { + for(SelectBody selBody:setOpList.getSelects()){ + if(selBody instanceof PlainSelect){ + PlainSelect selectClause=(PlainSelect) selBody; + normalizeSelectedColumnsForWithItem(withItem, selectClause); + } + else if(selBody instanceof SetOperationList){ + normalizeSelectedColumnsForWithItem(withItem, (SetOperationList)selBody); + } + } + } + + /** @author mathew + * deals with normalization of columns items in PlainSelect Queries. eg: With A(a) as (select name from ...) + * is normalized to With A(a) as (select name as a from ...) + * + */ + private void normalizeSelectedColumnsForWithItem(WithItem withItem, PlainSelect selectClause) { + // TODO Auto-generated method stub + if(withItem.getWithItemList()!=null &&!withItem.getWithItemList().isEmpty() ){ + for(int i=0;ii){ + SelectItem sItem=selectClause.getSelectItems().get(i); + if(sItem instanceof SelectExpressionItem){ + SelectExpressionItem selExpItem=(SelectExpressionItem)sItem; + Alias a = new Alias(withSelItem.toString()); + a.setUseAs(true); + selExpItem.setAlias(a); + } + } + } + } + } + + public SelectBody getSelectBody(){ + return selectBody; + } + + +} diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 6abfcb3a6..4f47a355b 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -74,6 +74,7 @@ import net.sf.jsqlparser.statement.select.*; import net.sf.jsqlparser.statement.truncate.*; import net.sf.jsqlparser.statement.update.*; import net.sf.jsqlparser.statement.merge.*; +import net.sf.jsqlparser.util.WithTransformation; import java.util.*; /** @@ -846,8 +847,11 @@ Select Select(): } SelectBody SelectBody(): -{ SelectBody selectBody = null; } +{ SelectBody selectBody = null; + List with = null; +} { + [ with=WithList() { } ] ( LOOKAHEAD(SetOperationList()) selectBody = SetOperationList() @@ -856,7 +860,12 @@ SelectBody SelectBody(): | ( "(" selectBody = PlainSelect() { ((PlainSelect)selectBody).setUseBrackets(true); } ")" ) ) - { return selectBody; } + { + if(with!=null){ + selectBody=new WithTransformation(selectBody,with).getSelectBody(); + } + return selectBody; + } } PlainSelect PlainSelect():