/* * 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.queryanswer.processor; import java.net.URI; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.slf4j.Logger; import com.obidea.semantika.database.sql.base.ISqlExpression; import com.obidea.semantika.database.sql.base.ISqlFunction; import com.obidea.semantika.database.sql.base.ISqlQuery; import com.obidea.semantika.database.sql.base.SqlJoinCondition; import com.obidea.semantika.database.sql.base.SqlSelectItem; import com.obidea.semantika.exception.SemantikaRuntimeException; import com.obidea.semantika.expression.base.AtomVisitorAdapter; import com.obidea.semantika.expression.base.IAtom; import com.obidea.semantika.expression.base.IFunction; import com.obidea.semantika.expression.base.ILiteral; import com.obidea.semantika.expression.base.IQueryExt; import com.obidea.semantika.expression.base.ITerm; import com.obidea.semantika.expression.base.IUriReference; import com.obidea.semantika.expression.base.IVariable; import com.obidea.semantika.expression.base.Join; import com.obidea.semantika.expression.base.Literal; import com.obidea.semantika.expression.base.QuerySet; import com.obidea.semantika.expression.base.TermUtils; import com.obidea.semantika.expression.base.UriReference; import com.obidea.semantika.knowledgebase.TermSubstitutionBinding; import com.obidea.semantika.knowledgebase.UnificationException; import com.obidea.semantika.knowledgebase.Unifier; import com.obidea.semantika.knowledgebase.model.IKnowledgeBase; import com.obidea.semantika.mapping.ImmutableMappingSet; import com.obidea.semantika.mapping.base.IMapping; import com.obidea.semantika.mapping.base.TripleAtom; import com.obidea.semantika.mapping.base.sql.SqlColumn; import com.obidea.semantika.mapping.base.sql.SqlEqualsTo; import com.obidea.semantika.mapping.base.sql.SqlFunction; 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.SqlSubQuery; import com.obidea.semantika.mapping.base.sql.SqlTable; import com.obidea.semantika.mapping.base.sql.SqlUserQuery; import com.obidea.semantika.mapping.base.sql.SqlValue; import com.obidea.semantika.util.CollectionUtils; import com.obidea.semantika.util.LogUtils; import com.obidea.semantika.util.RdfVocabulary; import com.obidea.semantika.util.Serializer; /** * WARNING: Reading the code below can do harm to your brain and cause a severe concussion! It's * full of magic and mystical algorithm. Do not *I repeat* DO NOT try to modify the code unless you * are more powerful than the great wizard! */ public class QueryUnfolder extends AtomVisitorAdapter implements IUnfolder { private static final URI RDF_TYPE = RdfVocabulary.TYPE.getUri(); private ImmutableMappingSet mMappingSet; private List<ISqlExpression> mPartialAnswers = new ArrayList<ISqlExpression>(); private List<TermSubstitutionBinding> mPartialBindings = new ArrayList<TermSubstitutionBinding>(); private Set<IVariable> mQueryVars = new HashSet<IVariable>(); private List<IVariable> mExcludeVariable = new ArrayList<IVariable>(); private NameGenerator mNameGenerator = new NameGenerator(); private boolean mWithinOptionalScope = false; private TermToSqlConverter mConverter = new TermToSqlConverter(); private static SqlMappingFactory sSqlFactory = SqlMappingFactory.getInstance(); private static final Logger LOG = LogUtils.createLogger("semantika.queryanswer"); //$NON-NLS-1$ /** * The sole constructor. * * @param kb * The <code>KnowledgeBase</code> object required for performing query unfolding. */ public QueryUnfolder(IKnowledgeBase kb) { mMappingSet = (ImmutableMappingSet) kb.getMappingSet(); } @Override public QuerySet<SqlQuery> unfold(QuerySet<? extends IQueryExt> querySet) throws QueryUnfoldingException { LOG.debug("Unfolding query..."); //$NON-NLS-1$ QuerySet<SqlQuery> toReturn = new QuerySet<SqlQuery>(); for (IQueryExt query : querySet.getAll()) { QuerySet<SqlQuery> sqlQueries = unfold(query); toReturn.copy(sqlQueries); } return toReturn; } @Override public QuerySet<SqlQuery> unfold(IQueryExt query) throws QueryUnfoldingException { /* * Make sure the helper lists and sets are empty before running the algorithm. */ reset(); initPartialBindings(); initIdentifierGenerator(); initExcludeVariableList(query); /* * The algorithm uses visitor pattern that traverses the tuple expression within * the query body. The unfolding for each tuple will produce a sub-query as * specified by the mapping assertion. */ try { for (IAtom tupleExpression : query.getBody()) { tupleExpression.accept(this); } } catch (SemantikaRuntimeException e) { /* * Catch any runtime exceptions and pass them as QueryUnfoldingException */ throw new QueryUnfoldingException(e.getMessage()); } QuerySet<SqlQuery> sqlQueries = new QuerySet<SqlQuery>(); buildAnswer(sqlQueries, query); return sqlQueries; } private void buildAnswer(QuerySet<SqlQuery> sqlQueries, IQueryExt query) { /* * The main feature of this algorithm is the handling of unifier binding. The * algorithm doesn't immediately process the variables in the query projection * and in the filter; only the variables in the tuple expressions. The former * variables are processed in the final step using the concluding unifier. * Moreover, this final step is responsible for constructing the unfolded query * (i.e., SQL query object). */ Iterator<TermSubstitutionBinding> partialBindingIter = mPartialBindings.iterator(); for (ISqlExpression partialAnswer : mPartialAnswers) { TermSubstitutionBinding partialBinding = partialBindingIter.next(); if (partialAnswer == null && partialBinding == null) { continue; } /* * Create a new SQL query object for each concluding unifier binding. */ SqlQuery sqlQuery = new SqlSelectQuery(query.isDistinct()); /* * Construct SQL SELECT. * * The column projection in SQL SELECT comes from the variables in SPARQL * projection. These variables were replaced to become column names through * the concluding unifier. */ for (ITerm term : query.getDistTerms()) { sqlQuery.addSelectItem(selectItem(term, partialBinding)); } /* * Construct SQL FROM. * * The table selection expressions are coming from the SubQueryQueue * that has collected Table expression or SubQuery expression. */ sqlQuery.setFromExpression(partialAnswer); /* * Construct SQL WHERE * * The filter expression can come from three sources: * 1) It can come from SPARQL FILTER expression. Similar to the construction * of SQL SELECT, the variables in SPARQL FILTER are replaced by the * concluding unifier to be column names. */ for (IFunction function : query.getFilters()) { IFunction filter = TermUtils.copy(function); // make copy filter.apply(partialBinding); sqlQuery.addWhereExpression(getSqlExpression(filter)); } /* * 2) The filtering can also come from SPARQL matching pattern, e.g., * SELECT ?v WHERE { ?v ?p "cat" }. This filtering is captured by the * unifier such that { VIEW1.COLUMN/"cat" } */ for (IVariable var : partialBinding.getVariables()) { assignValueFilters(var, partialBinding.getTerm(var), sqlQuery); } /* * 3) Some extra filters for handling NULL data in database. This filtering * is necessary due to SPARQL query semantic that is based on pattern * matching. */ for (IVariable var : getQueryVariables()) { assignNotNullFilters(partialBinding.getTerm(var), sqlQuery); } sqlQueries.add(sqlQuery); } } @Override public String getName() { return QueryUnfolder.class.getCanonicalName(); //$NON-NLS-1$ } @Override public void visit(IAtom atom) { if (atom instanceof Join) { Join join = (Join) atom; if (join.isInnerJoin()) { visitInnerJoinExpression(join); } else if (join.isLeftJoin()) { visitLeftJoinExpression(join); } } else if (atom instanceof TripleAtom) { visitTupleExpression((TripleAtom) atom); } } protected void visitInnerJoinExpression(Join join) { /* * Evaluate the partial answer on the left-side of the JOIN query */ List<ISqlExpression> leftPartialAnswers = getPartialAnswers(join.getLeftExpression()); /* * Evaluate the partial answer on the right-side of the JOIN query */ List<ISqlExpression> rightPartialAnswers = getPartialAnswers(join.getRightExpression()); /* * Create the SQL INNER JOIN expression using the left and right partial answers obtain from processing * each tuple node in join expression tree. */ int limit = leftPartialAnswers.size(); for (int i = 0; i < rightPartialAnswers.size(); i++) { ISqlExpression leftPartialAnswer = leftPartialAnswers.get(i % limit); // a trick to arrange the join expressions. ISqlExpression rightPartialAnswer = rightPartialAnswers.get(i); if (leftPartialAnswer != null && rightPartialAnswer != null) { ISqlExpression leftExpression = copy(leftPartialAnswer); ISqlExpression rightExpression = copy(rightPartialAnswer); SqlJoin sqlJoin = createSqlInnerJoin( leftExpression, rightExpression, joinConditions(mPartialBindings.get(i), CollectionUtils.union(scope(leftExpression), scope(rightExpression))), joinFilters(mPartialBindings.get(i), join.getFilter(), scope(rightExpression))); collectPartialAnswer(mPartialAnswers, sqlJoin); } else { collectPartialAnswer(mPartialAnswers, null); } } } protected void visitLeftJoinExpression(Join join) { /* * Evaluate the partial answer on the left-side of the OPTIONAL query */ List<ISqlExpression> leftPartialAnswers = getPartialAnswers(join.getLeftExpression()); List<TermSubstitutionBinding> leftPartialBindings = getPartialBindings(); initPartialBindings(); /* * Evaluate the partial answer on the right-side of the OPTIONAL query. It broadcasts a flag * to notify the evaluator that it is currently inside the optional scope. */ broadcastEnterOptionalScope(); List<ISqlExpression> rightPartialAnswers = getPartialAnswers(join.getRightExpression()); List<TermSubstitutionBinding> rightPartialBindings = getPartialBindings(); crossPartialBindings(leftPartialBindings, rightPartialBindings); /* * Create the SQL LEFT JOIN expression using the left and right partial answers obtain from processing * each tuple node in join expression tree. */ int limit = rightPartialAnswers.size(); for (int i = 0; i < leftPartialAnswers.size(); i++) { for (int j = 0; j < rightPartialAnswers.size(); j++) { ISqlExpression leftPartialAnswer = leftPartialAnswers.get(i); ISqlExpression rightPartialAnswer = rightPartialAnswers.get(j); if (leftPartialAnswer != null && rightPartialAnswer != null) { ISqlExpression leftExpression = copy(leftPartialAnswer); ISqlExpression rightExpression = copy(rightPartialAnswer); int bindingIndex = (i * limit) + j; SqlJoin sqlJoin = createSqlLeftJoin( leftExpression, rightExpression, joinConditions(mPartialBindings.get(bindingIndex), CollectionUtils.union(scope(leftExpression), scope(rightExpression))), joinFilters(mPartialBindings.get(bindingIndex), join.getFilter(), scope(rightExpression))); collectPartialAnswer(mPartialAnswers, sqlJoin); } else { collectPartialAnswer(mPartialAnswers, null); } } } /* * Reset the optional flag as it exits the optional scope. */ broadcastExitOptionalScope(); } private void crossPartialBindings(List<TermSubstitutionBinding> leftBindings, List<TermSubstitutionBinding> rightBindings) { for (TermSubstitutionBinding outerBinding : leftBindings) { for (TermSubstitutionBinding innerBinding : rightBindings) { if (outerBinding == null) { mPartialBindings.add(null); } else if (innerBinding == null) { mPartialBindings.add(null); } else { try { mPartialBindings.add(combine(copy(outerBinding), copy(innerBinding))); } catch (UnificationException e) { mPartialBindings.add(null); } } } } } private static TermSubstitutionBinding combine(TermSubstitutionBinding outerBinding, TermSubstitutionBinding innerBinding) throws UnificationException { for (IVariable var : innerBinding.getVariables()) { if (outerBinding.isBound(var)) { ITerm term1 = outerBinding.getTerm(var); ITerm term2 = innerBinding.getTerm(var); TermSubstitutionBinding newBinding = Unifier.findSubstitution(term1, term2); append(newBinding, outerBinding); } else { outerBinding.put(var, innerBinding.getTerm(var)); } } return outerBinding; } private static void append(TermSubstitutionBinding source, TermSubstitutionBinding target) { for (IVariable var : source.getVariables()) { target.put(var, source.getTerm(var)); } } private void broadcastEnterOptionalScope() { mWithinOptionalScope = true; } private void broadcastExitOptionalScope() { mWithinOptionalScope = false; } protected void visitTupleExpression(TripleAtom tuple) { collectQueryVariables(tuple); String identifier = getNextIdentifier(); List<TermSubstitutionBinding> partialBindings = getPartialBindings(); /* * Find candidate mappings that are matched to the given query tuple atom. */ for (IMapping mapping : findMappings(tuple)) { /* * Create a copy for every matched mapping before continue processing. */ TripleAtom targetAtom = copy(mapping.getTargetAtom()); SqlQuery sourceQuery = copy(mapping.getSourceQuery()); /* * Refresh the column variables in the target atom using the specified identifier. */ refreshVariables(targetAtom, identifier); /* * Refresh the column variables in the source query using the specified identifier. */ ISqlExpression partialAnswer = preparePartialAnswer(sourceQuery, identifier); for (TermSubstitutionBinding phi : partialBindings) { if (phi == null) { /* * When phi = null, the algorithm continues preserving this value and iterate * to the next phi value. */ collectPartialAnswer(mPartialAnswers, null); collectPartialBinding(mPartialBindings, null); continue; } try { /* * Apply the unifier to the tuple expression */ TripleAtom tupleAtom = applyUnifier(tuple, phi); /* * Check if the tuple atom (from query) and target atom (from mapping) can be * unified. */ TermSubstitutionBinding theta = Unifier.findSubstitution(targetAtom, tupleAtom); /* * If a unifier substitution was found then the tuple atom can be replaced (or * "unfolded") by the mapping's source query. */ collectPartialAnswer(mPartialAnswers, partialAnswer); /* * Compose the unifiers phi and theta to construct the concluding unifier. */ collectPartialBinding(mPartialBindings, compose(phi, theta)); } catch (UnificationException e) { /* * If findSubstitution() got failed, e.g., mismatch URI template or mismatch * constant value then put the partial answer and partial bindings as null. */ collectPartialAnswer(mPartialAnswers, null); collectPartialBinding(mPartialBindings, null); } } } } /* * Private utility methods */ private void reset() { mQueryVars.clear(); mPartialBindings.clear(); mPartialAnswers.clear(); mExcludeVariable.clear(); } private void initPartialBindings() { mPartialBindings.add(TermSubstitutionBinding.createEmptyBinding()); } private void initIdentifierGenerator() { mNameGenerator.reset(); } private void initExcludeVariableList(IQueryExt query) { for (IFunction filter : query.getFilters()) { for (ITerm term : filter.getParameters()) { if (term instanceof IVariable) { mExcludeVariable.add(TermUtils.asVariable(term)); } } } } private List<TermSubstitutionBinding> getPartialBindings() { List<TermSubstitutionBinding> toReturn = new ArrayList<TermSubstitutionBinding>(); CollectionUtils.move(mPartialBindings, toReturn); return toReturn; } private List<ISqlExpression> getPartialAnswers(IAtom queryAssertion) { queryAssertion.accept(this); List<ISqlExpression> toReturn = new ArrayList<ISqlExpression>(); CollectionUtils.move(mPartialAnswers, toReturn); return toReturn; } private void collectQueryVariables(TripleAtom tuple) { if (mWithinOptionalScope) { // == within the OPTIONAL scope, ignore variable return; } ITerm subject = TripleAtom.getSubject(tuple); if (subject instanceof IVariable) { IVariable subjectVar = TermUtils.asVariable(subject); if (!mExcludeVariable.contains(subjectVar)) { mQueryVars.add(subjectVar); } } ITerm object = TripleAtom.getObject(tuple); if (object instanceof IVariable) { IVariable objectVar = TermUtils.asVariable(object); if (!mExcludeVariable.contains(objectVar)) { mQueryVars.add(objectVar); } } } private Set<IVariable> getQueryVariables() { return mQueryVars; } private void refreshVariables(final TripleAtom targetAtom, final String viewName) { targetAtom.accept(new AtomVisitorAdapter() { @Override public void visit(IAtom atom) { if (atom instanceof TripleAtom) { TripleAtom targetAtom = (TripleAtom) atom; ITerm subject = TripleAtom.getSubject(targetAtom); subject.accept(this); ITerm object = TripleAtom.getObject(targetAtom); object.accept(this); } } @Override public void visit(IVariable variable) { if (variable instanceof SqlColumn) { SqlColumn column = (SqlColumn) variable; column.setViewName(viewName); // refresh variable name } } @Override public void visit(IFunction function) { for (ITerm parameter : function.getParameters()) { parameter.accept(this); } } }); } private ISqlExpression preparePartialAnswer(SqlQuery sourceQuery, String identifier) { if (isSimpleQuery(sourceQuery)) { /* * If the source query contains only a single table then assign this table with * the identifier. */ SqlTable table = sourceQuery.getAllTables().get(0); table.setAliasName(identifier); sourceQuery.changeAllColumnNamespace(table.getTableName(), identifier); return sourceQuery.getFromExpression(); // returns ISqlTable } else if (isUserQuery(sourceQuery)) { /* * If the source query contains a UserQuery object, i.e., a verbatim-unprocessed * SQL query string treated as a sub-query. */ SqlUserQuery userQuery = (SqlUserQuery) sourceQuery.getFromExpression(); userQuery.setViewName(identifier); sourceQuery.changeAllColumnNamespace(userQuery.getViewName(), identifier); return sourceQuery.getFromExpression(); } else { /* * The algorithm will use the full-qualified names as its ID so alias name is no * longer necessary. */ for (SqlSelectItem selectItem : sourceQuery.getSelectItems()) { selectItem.removeAliasName(); } /* * If the source query contains complex expressions, e.g., joins or filters * then all involving tables must be assigned to a new (inner) identifier. */ for (SqlTable table : sourceQuery.getAllTables()) { String nextIdentifier = getNextIdentifier(); table.setAliasName(nextIdentifier); sourceQuery.changeAllColumnNamespace(table.getTableName(), nextIdentifier); } /* * Return the original source query with the assigned identifier. */ return new SqlSubQuery(sourceQuery, identifier); } } private static boolean isSimpleQuery(ISqlQuery sourceQuery) { ISqlExpression fromExpression = sourceQuery.getFromExpression(); return ((fromExpression instanceof SqlTable) && !sourceQuery.hasWhereExpression()) ? true : false; } private static boolean isUserQuery(SqlQuery sourceQuery) { ISqlExpression fromExpression = sourceQuery.getFromExpression(); return (fromExpression instanceof SqlUserQuery) ? true : false; } private void collectPartialAnswer(List<ISqlExpression> partialAnswers, ISqlExpression partialAnswer) { partialAnswers.add(partialAnswer); } private void collectPartialBinding(List<TermSubstitutionBinding> partialBindings, TermSubstitutionBinding partialBinding) { partialBindings.add(partialBinding); } private static List<String> scope(ISqlExpression expression) { List<String> toReturn = new ArrayList<String>(); findScope(expression, toReturn); return toReturn; } private static void findScope(ISqlExpression expression, List<String> toReturn) { if (expression instanceof SqlJoin) { SqlJoin join = (SqlJoin) expression; findScope(join.getLeftExpression(), toReturn); findScope(join.getRightExpression(), toReturn); } else if (expression instanceof SqlTable) { SqlTable table = (SqlTable) expression; toReturn.add(table.getAliasName()); } else if (expression instanceof SqlSubQuery) { SqlSubQuery subQuery = (SqlSubQuery) expression; toReturn.add(subQuery.getViewName()); } } /* * Construct the join conditions (i.e., the ON expression) from the partial binding. * If the binding substitutes an SQL column object with another SQL column object * then this binding is a table join condition in SQL. In addition, this binding will * be removed from the partial binding cache. */ private Set<SqlJoinCondition> joinConditions(TermSubstitutionBinding binding, List<String> joinScope) { Set<SqlJoinCondition> toReturn = CollectionUtils.createEmptySet(SqlJoinCondition.class); List<IVariable> removeList = new ArrayList<IVariable>(); for (IVariable var : binding.getVariables()) { ITerm term = binding.getTerm(var); if (var instanceof SqlColumn && term instanceof SqlColumn) { SqlColumn c1 = (SqlColumn) var; SqlColumn c2 = (SqlColumn) term; if (withinScope(c1, joinScope) && withinScope(c2, joinScope)) { toReturn.add(new SqlJoinCondition(c1, c2)); removeList.add(var); // record the binding for removal } } } for (IVariable var : removeList) { binding.remove(var); } return toReturn; } /* * Construct the join filters (i.e., append the ON expression) from the partial binding. * If the binding substitutes an SQL column object with a constant and the column belongs * to the join scope then it is a join filter. In addition, this binding will be removed * from the partial binding cache. */ private Set<ISqlExpression> joinFilters(TermSubstitutionBinding binding, IFunction filter, List<String> filterScope) { Set<ISqlExpression> toReturn = CollectionUtils.createEmptySet(ISqlExpression.class); List<IVariable> removeList = new ArrayList<IVariable>(); for (IVariable var : binding.getVariables()) { ITerm term = binding.getTerm(var); if (var instanceof SqlColumn && term instanceof Literal) { SqlColumn c = (SqlColumn) var; if (withinScope(c, filterScope)) { toReturn.add(new SqlEqualsTo(c, getSqlExpression(term))); removeList.add(var); // record the binding for removal } } } for (IVariable var : removeList) { binding.remove(var); } if (filter != null) { filter.apply(binding); toReturn.add(getSqlExpression(filter)); } return toReturn; } private static boolean withinScope(SqlColumn c, List<String> joinScope) { return joinScope.contains(c.getViewName()) ? true : false; } private static SqlJoin createSqlInnerJoin(ISqlExpression leftExpression, ISqlExpression rightExpression, Set<SqlJoinCondition> joinConditions, Set<ISqlExpression> joinFilters) { SqlJoin sqlJoin = new SqlJoin(); sqlJoin.setInnerJoin(true); sqlJoin.setLeftExpression(leftExpression); sqlJoin.setRightExpression(rightExpression); sqlJoin.addJoinConditions(joinConditions); sqlJoin.addFilters(joinFilters); return sqlJoin; } private static SqlJoin createSqlLeftJoin(ISqlExpression leftExpression, ISqlExpression rightExpression, Set<SqlJoinCondition> joinConditions, Set<ISqlExpression> joinFilters) { SqlJoin sqlJoin = new SqlJoin(); sqlJoin.setLeftJoin(true); sqlJoin.setLeftExpression(leftExpression); sqlJoin.setRightExpression(rightExpression); sqlJoin.addJoinConditions(joinConditions); sqlJoin.addFilters(joinFilters); return sqlJoin; } private String getNextIdentifier() { return mNameGenerator.getNextUniqueName(); } private Set<IMapping> findMappings(TripleAtom tupleExpression) { URI signature = getTupleSignature(tupleExpression); Set<IMapping> matchedMappings = mMappingSet.get(signature); if (matchedMappings.isEmpty()) { throw new SemantikaRuntimeException("No mapping found for assertion: " + signature); //$NON-NLS-1$ } return matchedMappings; } private URI getTupleSignature(TripleAtom expr) { IUriReference predicate = TermUtils.asUriReference(TripleAtom.getPredicate(expr)); URI signature = UriReference.getUri(predicate); /* * If the expression is a class expression (i.e., predicate uses rdf:type) */ if (signature.equals(RDF_TYPE)) { IUriReference object = TermUtils.asUriReference(TripleAtom.getObject(expr)); signature = UriReference.getUri(object); } return signature; } /* * Private static utility methods */ private TermSubstitutionBinding compose(TermSubstitutionBinding phi, TermSubstitutionBinding theta) { TermSubstitutionBinding toReturn = copy(phi); // make copy toReturn.compose(theta); return toReturn; } private static TripleAtom applyUnifier(TripleAtom tupleExpression, TermSubstitutionBinding binding) { if (binding == null) { throw new NullPointerException("No binding was found"); //$NON-NLS-1$ } TripleAtom toReturn = copy(tupleExpression); // make copy toReturn.apply(binding); return toReturn; } private static ISqlExpression copy(ISqlExpression expression) { return (ISqlExpression) Serializer.copy(expression); } private static TripleAtom copy(TripleAtom expr) { return (TripleAtom) Serializer.copy(expr); } private static SqlQuery copy(SqlQuery sqlQuery) { return (SqlQuery) Serializer.copy(sqlQuery); } private static TermSubstitutionBinding copy(TermSubstitutionBinding binding) { TermSubstitutionBinding toReturn = TermSubstitutionBinding.createEmptyBinding(); for (IVariable var : binding.getVariables()) { IVariable varCopy = TermUtils.copy(var); ITerm termCopy = TermUtils.copy(binding.getTerm(var)); toReturn.put(varCopy, termCopy); } return toReturn; } private SqlSelectItem selectItem(ITerm term, TermSubstitutionBinding binding) { if (term instanceof IVariable) { IVariable var = (IVariable) term; if (binding.isBound(var)) { ITerm replacement = binding.getTerm(var); ISqlExpression expression = getSqlExpression(replacement); SqlSelectItem selectItem = new SqlSelectItem(expression); selectItem.setAliasName(var.getName()); return selectItem; } } throw new SemantikaRuntimeException("Unexpected term type found " + term.getClass().toString()); //$NON-NLS-1$ } private ISqlExpression getSqlExpression(ITerm term) { return mConverter.toSqlExpression(term); } private void assignValueFilters(IVariable var, ITerm term, SqlQuery sqlQuery) { if (var instanceof SqlColumn && term instanceof ILiteral) { SqlColumn column = (SqlColumn) var; SqlValue value = mConverter.toSqlValue((ILiteral) term); SqlFunction filter = sSqlFactory.createEqualsToExpression(column, value); sqlQuery.addWhereExpression(filter); } } private void assignNotNullFilters(ITerm term, SqlQuery sqlQuery) { if (term instanceof IVariable) { ISqlExpression expression = mConverter.toSqlExpression(term); ISqlFunction notNullFilter = sSqlFactory.createIsNotNullExpression(expression); sqlQuery.addWhereExpression(notNullFilter); } else if (term instanceof IFunction) { IFunction function = ((IFunction) term); for (ITerm t : function.getParameters()) { assignNotNullFilters(t, sqlQuery); } } } }