/*
* Copyright (c) 2013-2015 Josef Hardi <josef.hardi@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.obidea.semantika.database.sql.parser;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import net.sf.jsqlparser.expression.AllComparisonExpression;
import net.sf.jsqlparser.expression.AnalyticExpression;
import net.sf.jsqlparser.expression.AnyComparisonExpression;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.CaseExpression;
import net.sf.jsqlparser.expression.CastExpression;
import net.sf.jsqlparser.expression.DateValue;
import net.sf.jsqlparser.expression.DoubleValue;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.expression.ExtractExpression;
import net.sf.jsqlparser.expression.IntervalExpression;
import net.sf.jsqlparser.expression.JdbcNamedParameter;
import net.sf.jsqlparser.expression.JdbcParameter;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.OracleHierarchicalExpression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.SignedExpression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.TimeValue;
import net.sf.jsqlparser.expression.TimestampValue;
import net.sf.jsqlparser.expression.WhenClause;
import net.sf.jsqlparser.expression.operators.arithmetic.Addition;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor;
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
import net.sf.jsqlparser.expression.operators.arithmetic.Modulo;
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.Between;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
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.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.Matches;
import net.sf.jsqlparser.expression.operators.relational.MinorThan;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.FromItemVisitor;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.LateralSubSelect;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SelectVisitor;
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.ValuesList;
import net.sf.jsqlparser.statement.select.WithItem;
import com.obidea.semantika.database.IDatabaseMetadata;
import com.obidea.semantika.database.base.ITable;
import com.obidea.semantika.database.sql.base.ISqlExpression;
import com.obidea.semantika.database.sql.base.SqlJoinCondition;
import com.obidea.semantika.database.sql.base.SqlSelectItem;
import com.obidea.semantika.mapping.base.sql.SqlJoin;
import com.obidea.semantika.mapping.base.sql.SqlMappingFactory;
import com.obidea.semantika.mapping.base.sql.SqlQuery;
import com.obidea.semantika.mapping.base.sql.SqlSelectQuery;
import com.obidea.semantika.mapping.base.sql.SqlTable;
import com.obidea.semantika.util.Serializer;
public class SelectStatementHandler implements SelectVisitor, FromItemVisitor, ItemsListVisitor, ExpressionVisitor
{
private IDatabaseMetadata mMetadata;
private boolean mHasDistinct = false;
private SelectItemList mSelectItems = new SelectItemList();
private FromTablesList mTableList = new FromTablesList();
private ISqlExpression mExpression = null;
private ISqlExpression mFromExpression = null;
private Stack<ISqlExpression> mFilterExpressions = new Stack<ISqlExpression>();
private int mOpenParenthesisCount = 0;
private static SqlMappingFactory sSqlFactory = SqlMappingFactory.getInstance();
public SelectStatementHandler(IDatabaseMetadata metadata)
{
mMetadata = metadata;
}
public SqlQuery parse(Statement statement)
{
Select ss = (Select) statement;
ss.getSelectBody().accept(this);
SqlQuery query = new SqlSelectQuery(hasDistinct());
for (SqlSelectItem selectItem : getSelectItems()) {
query.addSelectItem(selectItem);
}
query.setFromExpression(getFromExpression());
for (ISqlExpression filter : getFilterExpressions()) {
query.addWhereExpression(filter);
}
return query;
}
@Override
public void visit(PlainSelect plainSelect)
{
/*
* Check if the query uses DISTINCT flag
*/
checkContainDistinct(plainSelect);
FromItem fromItem = plainSelect.getFromItem();
visitFromItemExpression(fromItem);
/*
* Collect the tables in the JOIN statement
*/
List<Join> joins = plainSelect.getJoins();
if (joins != null) {
for (Join join : joins) {
visitJoinExpression(join);
}
}
/*
* Collect the filter expressions in WHERE statement
*/
Expression expr = plainSelect.getWhere();
if (expr != null) {
visitWhereExpression(expr);
}
/*
* Collect the select item expressions in SELECT statement.
*/
List<SelectItem> selectItemExpressions = plainSelect.getSelectItems();
SelectItemHandler selectItemHandler = new SelectItemHandler(this);
selectItemHandler.parse(selectItemExpressions);
}
private boolean hasDistinct()
{
return mHasDistinct;
}
/* package */SelectItemList getSelectItems()
{
return mSelectItems;
}
/* package */FromTablesList getFromTablesList()
{
return mTableList;
}
private ISqlExpression getFromExpression()
{
return mFromExpression;
}
private Stack<ISqlExpression> getFilterExpressions()
{
return mFilterExpressions;
}
private void checkContainDistinct(PlainSelect plainSelect)
{
mHasDistinct = (plainSelect.getDistinct() == null) ? false : true;
}
private void visitFromItemExpression(FromItem fromItem)
{
fromItem.accept(this);
}
@Override
public void visit(ExpressionList exprList)
{
throw new UnsupportedSqlExpressionException("IN"); //$NON-NLS-1$
}
@Override
public void visit(net.sf.jsqlparser.schema.Table table)
{
String tn = table.getFullyQualifiedName();
ITable dboTable = findTableFromMetadata(tn);
if (dboTable != null) {
mTableList.add(dboTable);
}
mExpression = new SqlTable(dboTable);
/*
* To handle when the FROM expression consists only a single table
*/
if (mFromExpression == null) {
mFromExpression = new SqlTable(dboTable);
}
}
@Override
public void visit(SubSelect subSelect)
{
throw new UnsupportedSqlExpressionException("NESTED SELECT"); //$NON-NLS-1$
}
@Override
public void visit(SubJoin subJoin)
{
throw new UnsupportedSqlExpressionException("NESTED JOIN"); //$NON-NLS-1$
}
protected void visitJoinExpression(Join join)
{
if (join.isSimple()) {
throw new UnsupportedSqlExpressionException("SIMPLE JOIN"); //$NON-NLS-1$
}
else if (join.isInner()) {
ISqlExpression leftExpression = mFromExpression;
join.getRightItem().accept(this);
ISqlExpression rightExpression = getExpression();
JoinConditionHandler handler = new JoinConditionHandler(this);
join.getOnExpression().accept(handler);
createJoinExpression(leftExpression, rightExpression, handler.getJoinConditions());
}
else if (join.isOuter()) {
throw new UnsupportedSqlExpressionException("OUTER JOIN"); //$NON-NLS-1$
}
else if (join.isLeft()) {
throw new UnsupportedSqlExpressionException("LEFT JOIN"); //$NON-NLS-1$
}
else if (join.isRight()) {
throw new UnsupportedSqlExpressionException("RIGHT JOIN"); //$NON-NLS-1$
}
else if (join.isFull()) {
throw new UnsupportedSqlExpressionException("FULL JOIN"); //$NON-NLS-1$
}
else if (join.isNatural()) {
throw new UnsupportedSqlExpressionException("NATURAL JOIN"); //$NON-NLS-1$
}
else {
ISqlExpression leftExpression = mFromExpression;
join.getRightItem().accept(this);
ISqlExpression rightExpression = getExpression();
JoinConditionHandler handler = new JoinConditionHandler(this);
join.getOnExpression().accept(handler);
createJoinExpression(leftExpression, rightExpression, handler.getJoinConditions());
}
}
private void createJoinExpression(ISqlExpression leftExpression, ISqlExpression rightExpression, Set<SqlJoinCondition> joinConditions)
{
SqlJoin join = new SqlJoin();
join.setInnerJoin(true);
join.setLeftExpression(leftExpression);
join.setRightExpression(rightExpression);
join.addJoinConditions(joinConditions);
mFromExpression = join;
}
private void visitWhereExpression(Expression expr)
{
expr.accept(this);
}
@Override
public void visit(NullValue nullValue)
{
throw new UnsupportedSqlExpressionException("NULL VALUE"); //$NON-NLS-1$
}
@Override
public void visit(net.sf.jsqlparser.expression.Function function)
{
throw new UnsupportedSqlExpressionException("CALL FUNCTION"); //$NON-NLS-1$
}
@Override
public void visit(Parenthesis parenthesis)
{
increaseOpenParenthesisCount();
parenthesis.getExpression().accept(this);
}
@Override
public void visit(JdbcParameter jdbcParameter)
{
throw new UnsupportedSqlExpressionException("PREPARED STATEMENT"); //$NON-NLS-1$
}
@Override
public void visit(DoubleValue doubleValue)
{
double value = doubleValue.getValue();
mExpression = sSqlFactory.createNumericValueExpression(value);
}
@Override
public void visit(LongValue longValue)
{
long value = longValue.getValue();
mExpression = sSqlFactory.createNumericValueExpression(value);
}
@Override
public void visit(DateValue dateValue)
{
Date value = dateValue.getValue();
mExpression = sSqlFactory.createDateTimeValueExpression(value);
}
@Override
public void visit(TimeValue timeValue)
{
Time value = timeValue.getValue();
mExpression = sSqlFactory.createDateTimeValueExpression(value);
}
@Override
public void visit(TimestampValue timestampValue)
{
Timestamp value = timestampValue.getValue();
mExpression = sSqlFactory.createDateTimeValueExpression(value);
}
@Override
public void visit(StringValue stringValue)
{
String value = stringValue.getValue();
mExpression = sSqlFactory.createStringValueExpression(value);
}
@Override
public void visit(Addition addition)
{
throw new UnsupportedSqlExpressionException("ADDTION"); //$NON-NLS-1$
}
@Override
public void visit(Division division)
{
throw new UnsupportedSqlExpressionException("DIVISION"); //$NON-NLS-1$
}
@Override
public void visit(Multiplication multiplication)
{
throw new UnsupportedSqlExpressionException("MULTIPLICATION"); //$NON-NLS-1$
}
@Override
public void visit(Subtraction subtraction)
{
throw new UnsupportedSqlExpressionException("SUBSTRACT"); //$NON-NLS-1$
}
@Override
public void visit(AndExpression expression)
{
expression.getLeftExpression().accept(this);
expression.getRightExpression().accept(this);
if (mOpenParenthesisCount != 0) {
ISqlExpression rightExpression = mFilterExpressions.pop();
ISqlExpression leftExpression = mFilterExpressions.pop();
ISqlExpression andExpression = sSqlFactory.createAndExpression(leftExpression, rightExpression);
mFilterExpressions.push(andExpression);
}
if (mOpenParenthesisCount != 0) {
decreaseOpenParenthesisCount();
}
}
@Override
public void visit(OrExpression expression)
{
expression.getLeftExpression().accept(this);
expression.getRightExpression().accept(this);
ISqlExpression rightExpression = mFilterExpressions.pop();
ISqlExpression leftExpression = mFilterExpressions.pop();
ISqlExpression orExpression = sSqlFactory.createOrExpression(leftExpression, rightExpression);
mFilterExpressions.push(orExpression);
if (mOpenParenthesisCount != 0) {
decreaseOpenParenthesisCount();
}
}
@Override
public void visit(Between between)
{
throw new UnsupportedSqlExpressionException("BETWEEN"); //$NON-NLS-1$
}
@Override
public void visit(EqualsTo equalsTo)
{
mFilterExpressions.push(visitBinaryExpression(equalsTo));
}
@Override
public void visit(GreaterThan greaterThan)
{
mFilterExpressions.push(visitBinaryExpression(greaterThan));
}
@Override
public void visit(GreaterThanEquals greaterThanEquals)
{
mFilterExpressions.push(visitBinaryExpression(greaterThanEquals));
}
@Override
public void visit(MinorThan minorThan)
{
mFilterExpressions.push(visitBinaryExpression(minorThan));
}
@Override
public void visit(MinorThanEquals minorThanEquals)
{
mFilterExpressions.push(visitBinaryExpression(minorThanEquals));
}
@Override
public void visit(NotEqualsTo notEqualsTo)
{
mFilterExpressions.push(visitBinaryExpression(notEqualsTo));
}
@Override
public void visit(IsNullExpression isNullExpression)
{
isNullExpression.getLeftExpression().accept(this);
ISqlExpression parameter = getExpression();
boolean isNotNull = isNullExpression.isNot();
if (isNotNull) {
mFilterExpressions.push(sSqlFactory.createIsNotNullExpression(parameter));
}
else {
mFilterExpressions.push(sSqlFactory.createIsNullExpression(parameter));
}
}
@Override
public void visit(InExpression inExpression)
{
throw new UnsupportedSqlExpressionException("IN"); //$NON-NLS-1$
}
@Override
public void visit(LikeExpression likeExpression)
{
throw new UnsupportedSqlExpressionException("LIKE"); //$NON-NLS-1$
}
@Override
public void visit(net.sf.jsqlparser.schema.Column tableColumn)
{
String tableName = tableColumn.getTable().getFullyQualifiedName();
String columnName = tableColumn.getColumnName();
mExpression = mTableList.getColumn(tableName, columnName);
}
/*
* Get the Table object given its full name.
*/
private ITable findTableFromMetadata(String tableName)
{
return mMetadata.getTable(tableName);
}
@Override
public void visit(CaseExpression caseExpression)
{
throw new UnsupportedSqlExpressionException("CASE"); //$NON-NLS-1$
}
@Override
public void visit(WhenClause whenClause)
{
throw new UnsupportedSqlExpressionException("WHEN"); //$NON-NLS-1$
}
@Override
public void visit(ExistsExpression existsExpression)
{
throw new UnsupportedSqlExpressionException("EXISTS"); //$NON-NLS-1$
}
@Override
public void visit(AllComparisonExpression allComparisonExpression)
{
throw new UnsupportedSqlExpressionException("NESTED SELECT"); //$NON-NLS-1$
}
@Override
public void visit(AnyComparisonExpression anyComparisonExpression)
{
throw new UnsupportedSqlExpressionException("NESTED SELECT"); //$NON-NLS-1$
}
@Override
public void visit(Concat concat)
{
throw new UnsupportedSqlExpressionException("CONCAT"); //$NON-NLS-1$
}
@Override
public void visit(Matches matches)
{
throw new UnsupportedSqlExpressionException("MATCHES"); //$NON-NLS-1$
}
@Override
public void visit(BitwiseAnd bitwiseAnd)
{
throw new UnsupportedSqlExpressionException("BITWISE AND"); //$NON-NLS-1$
}
@Override
public void visit(BitwiseOr bitwiseOr)
{
throw new UnsupportedSqlExpressionException("BITWISE OR"); //$NON-NLS-1$
}
@Override
public void visit(BitwiseXor bitwiseXor)
{
throw new UnsupportedSqlExpressionException("BITWISE XOR"); //$NON-NLS-1$
}
@Override
public void visit(SignedExpression signed)
{
throw new UnsupportedSqlExpressionException("SIGNED"); //$NON-NLS-1$
}
@Override
public void visit(JdbcNamedParameter parameter)
{
throw new UnsupportedSqlExpressionException("JDBC NAMED PARAMETER"); //$NON-NLS-1$
}
@Override
public void visit(CastExpression cast)
{
throw new UnsupportedSqlExpressionException("CAST"); //$NON-NLS-1$
}
@Override
public void visit(Modulo module)
{
throw new UnsupportedSqlExpressionException("MODULO"); //$NON-NLS-1$
}
@Override
public void visit(AnalyticExpression analytic)
{
throw new UnsupportedSqlExpressionException("ANALYTIC"); //$NON-NLS-1$
}
@Override
public void visit(ExtractExpression extract)
{
throw new UnsupportedSqlExpressionException("EXTRACT"); //$NON-NLS-1$
}
@Override
public void visit(IntervalExpression interval)
{
throw new UnsupportedSqlExpressionException("INTERVAL"); //$NON-NLS-1$
}
@Override
public void visit(OracleHierarchicalExpression oracleHierarchical)
{
throw new UnsupportedSqlExpressionException("ORACLE HIERARCHICAL"); //$NON-NLS-1$
}
@Override
public void visit(RegExpMatchOperator regExpMatch)
{
throw new UnsupportedSqlExpressionException("REGEXP MATCH"); //$NON-NLS-1$
}
@Override
public void visit(MultiExpressionList multiExpressionList)
{
throw new UnsupportedSqlExpressionException("MULTI EXPRESSION LIST"); //$NON-NLS-1$
}
@Override
public void visit(LateralSubSelect lateralSubSelect)
{
throw new UnsupportedSqlExpressionException("LATERAL SUB-SELECT"); //$NON-NLS-1$
}
@Override
public void visit(ValuesList valuesList)
{
throw new UnsupportedSqlExpressionException("VALUES LIST"); //$NON-NLS-1$
}
@Override
public void visit(SetOperationList setOperationList)
{
throw new UnsupportedSqlExpressionException("SET OPERATION LIST"); //$NON-NLS-1$
}
@Override
public void visit(WithItem withItem)
{
throw new UnsupportedSqlExpressionException("WITH ITEM"); //$NON-NLS-1$
}
protected ISqlExpression visitBinaryExpression(BinaryExpression binaryExpression)
{
binaryExpression.getLeftExpression().accept(this);
ISqlExpression leftParameter = getExpression();
binaryExpression.getRightExpression().accept(this);
ISqlExpression rightParameter = getExpression();
if (binaryExpression instanceof EqualsTo) {
return sSqlFactory.createEqualsToExpression(leftParameter, rightParameter);
}
else if (binaryExpression instanceof NotEqualsTo) {
return sSqlFactory.createNotEqualsToExpression(leftParameter, rightParameter);
}
else if (binaryExpression instanceof GreaterThan) {
return sSqlFactory.createGreaterThanExpression(leftParameter, rightParameter);
}
else if (binaryExpression instanceof GreaterThanEquals) {
return sSqlFactory.createGreaterThanEqualsExpression(leftParameter, rightParameter);
}
else if (binaryExpression instanceof MinorThan) {
return sSqlFactory.createLessThanExpression(leftParameter, rightParameter);
}
else if (binaryExpression instanceof MinorThanEquals) {
return sSqlFactory.createLessThanEqualsExpression(leftParameter, rightParameter);
}
throw new UnsupportedSqlExpressionException(binaryExpression.toString());
}
private ISqlExpression getExpression()
{
return (ISqlExpression) Serializer.copy(mExpression);
}
private void increaseOpenParenthesisCount()
{
mOpenParenthesisCount++;
}
private void decreaseOpenParenthesisCount()
{
mOpenParenthesisCount--;
}
}