/* * 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.deparser; import java.sql.Types; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import com.obidea.semantika.database.sql.base.ISqlBinaryFunction; import com.obidea.semantika.database.sql.base.ISqlColumn; import com.obidea.semantika.database.sql.base.ISqlExpression; import com.obidea.semantika.database.sql.base.ISqlExpressionVisitor; import com.obidea.semantika.database.sql.base.ISqlFunction; import com.obidea.semantika.database.sql.base.ISqlJoin; import com.obidea.semantika.database.sql.base.ISqlQuery; import com.obidea.semantika.database.sql.base.ISqlSubQuery; import com.obidea.semantika.database.sql.base.ISqlTable; import com.obidea.semantika.database.sql.base.ISqlUnaryFunction; import com.obidea.semantika.database.sql.base.ISqlValue; import com.obidea.semantika.database.sql.base.SqlJoinCondition; import com.obidea.semantika.database.sql.base.SqlSelectItem; import com.obidea.semantika.database.sql.dialect.IDialect; import com.obidea.semantika.datatype.DataType; import com.obidea.semantika.expression.base.QuerySet; import com.obidea.semantika.mapping.base.sql.SqlAddition; import com.obidea.semantika.mapping.base.sql.SqlAnd; import com.obidea.semantika.mapping.base.sql.SqlColumn; import com.obidea.semantika.mapping.base.sql.SqlConcat; import com.obidea.semantika.mapping.base.sql.SqlDivide; import com.obidea.semantika.mapping.base.sql.SqlEqualsTo; import com.obidea.semantika.mapping.base.sql.SqlGreaterThan; import com.obidea.semantika.mapping.base.sql.SqlGreaterThanEquals; import com.obidea.semantika.mapping.base.sql.SqlIsNotNull; import com.obidea.semantika.mapping.base.sql.SqlIsNull; import com.obidea.semantika.mapping.base.sql.SqlLang; import com.obidea.semantika.mapping.base.sql.SqlLessThan; import com.obidea.semantika.mapping.base.sql.SqlLessThanEquals; import com.obidea.semantika.mapping.base.sql.SqlMultiply; import com.obidea.semantika.mapping.base.sql.SqlNotEqualsTo; import com.obidea.semantika.mapping.base.sql.SqlOr; import com.obidea.semantika.mapping.base.sql.SqlRegex; import com.obidea.semantika.mapping.base.sql.SqlStr; import com.obidea.semantika.mapping.base.sql.SqlSubtract; import com.obidea.semantika.mapping.base.sql.SqlUriConcat; import com.obidea.semantika.mapping.base.sql.SqlUserQuery; public class SqlDeparser extends TextFormatter implements ISqlDeparser, ISqlExpressionVisitor { private IDialect mDialect; private String mExpressionString = ""; //$NON-NLS-1$ public SqlDeparser(IDialect dialect) { mDialect = dialect; } private SqlDeparser(SqlDeparser parent) { mDialect = parent.mDialect; mTabCounter = parent.mTabCounter; } @Override public String deparse(QuerySet<? extends ISqlQuery> querySet) { StringBuilder unions = new StringBuilder(); boolean needUnion = false; for (ISqlQuery query : querySet.getAll()) { if (needUnion) { unions.append("\n"); unions.append(Sql99.UNION); unions.append("\n"); } unions.append(deparse(query)); needUnion = true; } return unions.toString(); } @Override public String deparse(ISqlQuery query) { initStringBuilder(); visitSelect(query.getSelectItems(), query.isDistinct()); visitFrom(query.getFromExpression()); if (query.hasWhereExpression()) { visitWhere(query.getWhereExpression()); } return flushStringBuilder(); } private void visitSelect(List<SqlSelectItem> selectItemList, boolean isDistinct) { append(Sql99.SELECT); space(); if (isDistinct) { append(Sql99.DISTINCT); space(); } boolean needComma = false; boolean needShift = true; SelectItemVisitor selectItemVisitor = new SelectItemVisitor(); for (SqlSelectItem selectItem : selectItemList) { if (needComma) { append(","); //$NON-NLS-1$ newline(); if (needShift) { shiftRight(); } needShift = false; } append(str(selectItem.getExpression(), selectItemVisitor)); if (selectItem.hasAliasName()) { space(); append(mDialect.alias(selectItem.getAliasName())); } needComma = true; } newline(); shiftLeft(); } private void visitFrom(ISqlExpression fromExpression) { append(Sql99.FROM); space(); fromExpression.accept(this); newline(); } private void visitWhere(Set<ISqlExpression> whereExpressions) { append(Sql99.WHERE); space(); boolean needAnd = false; boolean needShift = true; for (ISqlExpression whereExpression : whereExpressions) { if (needAnd) { space(); append(Sql99.AND); newline(); if (needShift) { shiftRight(); } needShift = false; } append(str(whereExpression)); needAnd = true; } newline(); shiftLeft(); } private String str(ISqlExpression expression) { return str(expression, this); } private String str(ISqlExpression expression, ISqlExpressionVisitor visitor) { expression.accept(visitor); return mExpressionString; } @Override public void visit(ISqlTable table) { append(mDialect.identifier(table.getNameFragments())); if (table.hasAliasName()) { space(); append(mDialect.alias(table.getAliasName())); } } @Override public void visit(ISqlColumn column) { if (((SqlColumn) column).isOverriden()) { visitOverridenColumn(column); } else { visitColumn(column); } } private void visitOverridenColumn(ISqlColumn column) { mExpressionString = mDialect.cast(mDialect.identifier(column.getNameFragments()), column.getColumnType()); } private void visitColumn(ISqlColumn column) { mExpressionString = mDialect.identifier(column.getNameFragments()); } @Override public void visit(ISqlFunction function) { if (function instanceof ISqlUnaryFunction) { visitSqlUnaryFunctionExpression((ISqlUnaryFunction) function); } else if (function instanceof ISqlBinaryFunction) { visitSqlBinaryFunctionExpression((ISqlBinaryFunction) function); } else { visitSqlNaryFunctionExpression(function); } } private void visitSqlUnaryFunctionExpression(ISqlUnaryFunction unaryFunction) { String argument = str(unaryFunction.getParameterExpression()); if (unaryFunction instanceof SqlIsNull) { mExpressionString = mDialect.isNull(argument); } else if (unaryFunction instanceof SqlIsNotNull) { mExpressionString = mDialect.isNotNull(argument); } else if (unaryFunction instanceof SqlLang) { mExpressionString = mDialect.lang(argument); } else if (unaryFunction instanceof SqlStr) { mExpressionString = mDialect.cast(argument, Types.VARCHAR); } else { throw unknownSqlExpressionException(unaryFunction); } } private void visitSqlBinaryFunctionExpression(ISqlBinaryFunction binaryFunction) { String leftArgument = str(binaryFunction.getLeftParameterExpression()); String rightArgument = str(binaryFunction.getRightParameterExpression()); if (binaryFunction instanceof SqlAddition) { mExpressionString = mDialect.add(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlSubtract) { mExpressionString = mDialect.subtract(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlMultiply) { mExpressionString = mDialect.multiply(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlDivide) { mExpressionString = mDialect.divide(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlEqualsTo) { mExpressionString = mDialect.equals(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlNotEqualsTo) { mExpressionString = mDialect.notEquals(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlGreaterThan) { mExpressionString = mDialect.greaterThan(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlGreaterThanEquals) { mExpressionString = mDialect.greaterThanEquals(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlLessThan) { mExpressionString = mDialect.lessThan(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlLessThanEquals) { mExpressionString = mDialect.lessThanEquals(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlAnd) { mExpressionString = mDialect.and(leftArgument, rightArgument); } else if (binaryFunction instanceof SqlOr) { mExpressionString = mDialect.or(leftArgument, rightArgument); } else { throw unknownSqlExpressionException(binaryFunction); } } private void visitSqlNaryFunctionExpression(ISqlFunction naryFunction) { if (naryFunction instanceof SqlRegex) { visitSqlRegex((SqlRegex) naryFunction); } else { throw unknownSqlExpressionException(naryFunction); } } private void visitSqlRegex(SqlRegex sqlRegex) { String[] arguments = new String[] { "", "", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ int i = 0; for (ISqlExpression expression : sqlRegex.getParameterExpressions()) { arguments[i++] = str(expression); } mExpressionString = mDialect.regex(arguments[0], arguments[1], arguments[2]); } @Override public void visit(ISqlValue value) { visitLiteral(value); } private void visitLiteral(ISqlValue value) { String lexicalValue = value.getValue(); String datatype = value.getDatatype(); if (DataType.NUMERIC_TYPES.contains(datatype)) { mExpressionString = lexicalValue; } else { /* * Any sequence of characters delimited by single quotes. If the single * quote character is included in the sequence it must be written twice. */ lexicalValue = lexicalValue.replaceAll("'", "''"); //$NON-NLS-1$ //$NON-NLS-2% mExpressionString = mDialect.literal(lexicalValue); } } @Override public void visit(ISqlJoin joinExpression) { /* * Join left expression */ joinExpression.getLeftExpression().accept(this); newline(); /* * Print the JOIN type keyword */ if (joinExpression.isInnerJoin()) { append(Sql99.INNER_JOIN); } else if (joinExpression.isLeftJoin()) { append(Sql99.LEFT_JOIN); } space(); /* * Join right expression. If the right expression is a join expression then * it will be printed as an inner join expression. */ if (hasInnerJoin(joinExpression)) { append("("); //$NON-NLS-1$ newline(); shiftRight(); // tab joinExpression.getRightExpression().accept(this); newline(); shiftLeft(); // restore tab append(")"); //$NON-NLS-1$ } else { joinExpression.getRightExpression().accept(this); } /* * Print the join conditions. */ space(); append(Sql99.ON); space(); boolean hasOnExpression = false; boolean needAnd = false; if (joinExpression.hasJoinConditions()) { hasOnExpression = true; for (SqlJoinCondition joinCondition : joinExpression.getJoinConditions()) { if (needAnd) { space(); append(Sql99.AND); space(); } append(str(joinCondition.getLeftColumn())); space(); append(Sql99.EQ); space(); append(str(joinCondition.getRightColumn())); needAnd = true; } } if (joinExpression.hasFilters()) { hasOnExpression = true; for (ISqlExpression filter : joinExpression.getFilters()) { if (needAnd) { space(); append(Sql99.AND); space(); } append(str(filter)); needAnd = true; } } if (!hasOnExpression) { append(Sql99.TRUE); } } private boolean hasInnerJoin(ISqlJoin expression) { boolean toReturn = false; if (expression.getRightExpression() instanceof ISqlJoin) { toReturn = true; } return toReturn; } @Override public void visit(ISqlSubQuery subQueryExpression) { append(Sql99.LPAREN); newline(); shiftRight(); // tab if (subQueryExpression instanceof SqlUserQuery) { SqlUserQuery userQuery = (SqlUserQuery) subQueryExpression; append(userQuery.getSqlString()); } else { SqlDeparser innerDeparser = new SqlDeparser(this); append(innerDeparser.deparse(subQueryExpression.getQuery())); } newline(); shiftLeft(); // restore tab append(Sql99.RPAREN); space(); append(Sql99.AS); space(); append(mDialect.view(subQueryExpression.getViewName())); } private SqlDeparserException unknownSqlExpressionException(ISqlFunction sqlFunction) { return new SqlDeparserException("Unable to produce SQL function expression: " + sqlFunction); //$NON-NLS-1$ } class SelectItemVisitor implements ISqlExpressionVisitor { @Override public void visit(ISqlColumn column) { mExpressionString = mDialect.identifier(column.getNameFragments()); } @Override public void visit(ISqlFunction function) { if (function instanceof SqlConcat) { visitSqlConcat((SqlConcat) function); } else if (function instanceof SqlUriConcat) { visitSqlUriConcat((SqlUriConcat) function); } else { throw new SqlDeparserException("Unable to produce SQL select item expression: " + function); //$NON-NLS-1$ } } private void visitSqlConcat(SqlConcat sqlConcat) { List<String> arguments = new ArrayList<String>(); for (ISqlExpression expression : sqlConcat.getParameterExpressions()) { arguments.add(str(expression)); } mExpressionString = mDialect.concat(arguments); } private void visitSqlUriConcat(SqlUriConcat sqlUriConcat) { List<String> arguments = new ArrayList<String>(); Iterator<ISqlExpression> iter = sqlUriConcat.getParameterExpressions().iterator(); ISqlValue stringTemplateValue = (ISqlValue) iter.next(); // must be a value arguments.add(mDialect.literal(stringTemplateValue.getValue())); arguments.add("' : '"); //$NON-NLS-1$ arguments.add("'\"'"); //$NON-NLS-1$ boolean needSeparator = false; while (iter.hasNext()) { if (needSeparator) { arguments.add("'\" \"'"); //$NON-NLS-1$ } ISqlExpression expression = iter.next(); arguments.add(str(expression)); needSeparator = true; } arguments.add("'\"'"); //$NON-NLS-1$ mExpressionString = mDialect.concat(arguments); } @Override public void visit(ISqlValue value) { visitLiteral(value); } @Override public void visit(ISqlTable tableExpression) { // NO-OP } @Override public void visit(ISqlJoin joinExpression) { // NO-OP } @Override public void visit(ISqlSubQuery subQueryExpression) { // NO-OP } } }