/******************************************************************************* * Copyright (c) 2008 Cambridge Semantics Incorporated. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Cambridge Semantics Incorporated - initial API and implementation *******************************************************************************/ package org.openanzo.jdbc.container.query; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections15.MultiMap; import org.apache.commons.lang.StringUtils; import org.openanzo.analysis.RequestAnalysis; import org.openanzo.exceptions.AnzoException; import org.openanzo.exceptions.ExceptionConstants; import org.openanzo.exceptions.LogUtils; import org.openanzo.glitter.exception.GlitterException; import org.openanzo.glitter.exception.GlitterRuntimeException; import org.openanzo.glitter.exception.UnknownGraphException; import org.openanzo.glitter.expression.BinaryFunction; import org.openanzo.glitter.expression.UnaryFunction; import org.openanzo.glitter.expression.builtin.IsBlank; import org.openanzo.glitter.expression.builtin.IsIRI; import org.openanzo.glitter.expression.builtin.IsLiteral; import org.openanzo.glitter.expression.builtin.LogicalAnd; import org.openanzo.glitter.expression.builtin.Not; import org.openanzo.glitter.expression.builtin.PolymorphicEq; import org.openanzo.glitter.expression.builtin.PolymorphicNe; import org.openanzo.glitter.expression.builtin.SameTerm; import org.openanzo.glitter.query.AbstractSolutionGenerator; import org.openanzo.glitter.query.FunctionalPredicate; import org.openanzo.glitter.query.PatternSolutionImpl; import org.openanzo.glitter.query.QueryController; import org.openanzo.glitter.query.SolutionList; import org.openanzo.glitter.query.SolutionSet; import org.openanzo.glitter.query.SolutionUtils; import org.openanzo.glitter.query.planning.QueryOptimizer; import org.openanzo.glitter.query.planning.TripleNode; import org.openanzo.glitter.syntax.abstrakt.BGP; import org.openanzo.glitter.syntax.abstrakt.Expression; import org.openanzo.glitter.syntax.abstrakt.FunctionCall; import org.openanzo.glitter.syntax.abstrakt.GraphPattern; import org.openanzo.glitter.syntax.abstrakt.Group; import org.openanzo.glitter.syntax.abstrakt.Optional; import org.openanzo.glitter.syntax.abstrakt.SimpleExpression; import org.openanzo.glitter.syntax.abstrakt.TreeNode; import org.openanzo.glitter.syntax.abstrakt.TriplePatternNode; import org.openanzo.jdbc.container.CoreDBConfiguration; import org.openanzo.jdbc.container.sql.GlitterSQL; import org.openanzo.jdbc.layout.CompositeNodeLayout; import org.openanzo.jdbc.query.NoSolutionsException; import org.openanzo.jdbc.query.NodeConverter; import org.openanzo.jdbc.query.SQLQueryConstants; import org.openanzo.jdbc.utils.PreparedStatementProvider; import org.openanzo.jdbc.utils.RdbException; import org.openanzo.rdf.Bindable; import org.openanzo.rdf.TriplePattern; import org.openanzo.rdf.TriplePatternComponent; import org.openanzo.rdf.URI; import org.openanzo.rdf.Variable; import org.openanzo.rdf.Constants.GRAPHS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link AnzoSolutionGeneratorBase} is a base class for Anzo Glitter implementations that share similar approaches to solving Glitter queries. * * @author lee <lee@cambridgesemantics.com> * */ public abstract class AnzoSolutionGeneratorBase extends AbstractSolutionGenerator { private static final org.slf4j.Logger log = LoggerFactory.getLogger(AnzoSolutionGeneratorBase.class); protected boolean includeAllNamedGraphsInDefaultDataset = false, includeAllMetadataGraphsInDefaultDataset = false, includeAllNamedGraphsInNamedDataset = false, includeAllMetadataGraphsInNamedDataset = false; // These allow us to short circuit some SQL queries. protected boolean noDefaultGraphs; protected boolean noNamedGraphs; protected NodeConverter nodeConverter = null; // These variables are the counts of valid (with respect to ACLs and time) graphs // in the default graph and named graph parts of the query's dataset protected long validGraphsInDefaultGraph = -1; protected long validGraphsInNamedGraphs = -1; abstract protected CoreDBConfiguration getConfiguration(); abstract protected AnzoBGPQuery createAnzoBGPQuery(TreeNode node); abstract protected Connection getConnection(); abstract protected PreparedStatementProvider getPreparedStatementProvider(); abstract protected Logger getLogger(); abstract protected String getSessionPrefix(); abstract protected void clearTempTable() throws RdbException, SQLException; abstract protected CompositeNodeLayout getNodeLayout(); abstract protected String getDefaultGraphsTempTable(); abstract protected String getNamedGraphsTempTable(); abstract protected String getTemporaryTempTable(); abstract protected int insertGraphByIdIfValid(Long graphId, String insertTable, boolean defaults) throws SQLException, GlitterException, AnzoException; abstract protected boolean insertGraphsIfValid(Set<URI> graphs, String insertTable, boolean defaults) throws SQLException, GlitterException, AnzoException; abstract protected int insertAllGraphsIfValid(String insertTable, boolean defaults) throws SQLException, GlitterException, AnzoException; abstract protected int insertDatasetGraphsIfValid(Long datasetId, Long datasetGraphPropertyId, String insertTable, boolean defaults) throws SQLException, GlitterException, AnzoException; abstract protected int insertAllNamedGraphsIfValid(String insertTable, boolean defaults) throws SQLException, GlitterException, AnzoException; abstract protected int insertAllMetadataGraphsIfValid(String insertTable, boolean defaults) throws SQLException, GlitterException, AnzoException; abstract protected void setGraphsType(boolean defaults, GraphSetType type); protected AnzoBGPQuery getAnzoBGPQuery(TreeNode node, org.openanzo.rdf.URI namedGraph, Variable namedGraphVariable, SolutionSet requiredBindings) throws NoSolutionsException { AnzoBGPQuery query = createAnzoBGPQuery(node); query.setThisNode(node); query.setDefaultGraphCount(this.validGraphsInDefaultGraph); if (namedGraph != null) { query.setNamedGraph(namedGraph); } else if (namedGraphVariable != null) { query.setGraphVariable(namedGraphVariable); } query.setRequiredBindings(requiredBindings); return query; } public void initialize() throws GlitterException { boolean isEnabled = RequestAnalysis.getAnalysisLogger().isDebugEnabled(); long start = 0; if (isEnabled) { start = System.currentTimeMillis(); } try { long[] validUris = populateDatasetTables(getNodeLayout(), getConnection(), getDefaultGraphsTempTable(), getNamedGraphsTempTable(), getLogger()); validGraphsInDefaultGraph = validUris[0]; validGraphsInNamedGraphs = validUris[1]; } finally { if (isEnabled) { RequestAnalysis.getAnalysisLogger().debug("[glitter_AnzoSolutionGeneratorBase_initializeDatasetTables] {}", Long.toString(System.currentTimeMillis() - start)); } } } protected long[] populateDatasetTables(CompositeNodeLayout layout, Connection connection, String defaultGraphsTempTable, String namedGraphsTempTable, Logger log) throws GlitterException { long validNamedGraphsInDefaultGraph = 0; long validNamedGraphsInNamedGraphs = 0; Set<URI> defaultGraphSet = null, namedGraphSet = null; try { Set<URI> defaultGraphs = this.dataset.getDefaultGraphURIs(), namedGraphs = this.dataset.getNamedGraphURIs(); // translate the list of URIs to lists of node IDs. If any of the graphs // are unknown to the system we raise an error. if (defaultGraphs != null) { if (defaultGraphs.contains(GRAPHS.ALL_GRAPHS) || defaultGraphs.contains(GRAPHS.ALL_METADATAGRAPHS) || defaultGraphs.contains(GRAPHS.ALL_NAMEDGRAPHS)) { defaultGraphSet = new HashSet<URI>(); for (URI u : defaultGraphs) { if (u.equals(GRAPHS.ALL_GRAPHS)) { includeAllNamedGraphsInDefaultDataset = includeAllMetadataGraphsInDefaultDataset = true; } else if (u.equals(GRAPHS.ALL_METADATAGRAPHS)) { includeAllMetadataGraphsInDefaultDataset = true; } else if (u.equals(GRAPHS.ALL_NAMEDGRAPHS)) { includeAllNamedGraphsInDefaultDataset = true; } else { defaultGraphSet.add(u); } } } else { defaultGraphSet = defaultGraphs; } } else { defaultGraphSet = Collections.<URI> emptySet(); } if (namedGraphs != null) { if (namedGraphs.contains(GRAPHS.ALL_GRAPHS) || namedGraphs.contains(GRAPHS.ALL_METADATAGRAPHS) || namedGraphs.contains(GRAPHS.ALL_NAMEDGRAPHS)) { namedGraphSet = new HashSet<URI>(); for (URI u : namedGraphs) { if (u.equals(GRAPHS.ALL_GRAPHS)) includeAllNamedGraphsInNamedDataset = includeAllMetadataGraphsInNamedDataset = true; else if (u.equals(GRAPHS.ALL_METADATAGRAPHS)) includeAllMetadataGraphsInNamedDataset = true; else if (u.equals(GRAPHS.ALL_NAMEDGRAPHS)) includeAllNamedGraphsInNamedDataset = true; else namedGraphSet.add(u); } } else { namedGraphSet = namedGraphs; } } else { namedGraphSet = Collections.<URI> emptySet(); } // get the graph IDs to operate on into two tables (separate for default // graphs and named graphs) -- further queries can ignore ACLs, // transactionTime of named graphs, and the user-specified dataset. validNamedGraphsInDefaultGraph = populateValidGraphs(layout, connection, defaultGraphSet, true, includeAllNamedGraphsInDefaultDataset, includeAllMetadataGraphsInDefaultDataset, defaultGraphsTempTable); this.noDefaultGraphs = validNamedGraphsInDefaultGraph == 0; validNamedGraphsInNamedGraphs = populateValidGraphs(layout, connection, namedGraphSet, false, includeAllNamedGraphsInNamedDataset, includeAllMetadataGraphsInNamedDataset, namedGraphsTempTable); this.noNamedGraphs = validNamedGraphsInNamedGraphs == 0; } catch (SQLException e) { log.error(LogUtils.RDB_MARKER, "SQL error populating dataset tables", e); throw new GlitterRuntimeException(ExceptionConstants.GLITTER.SQL_EXCEPTION, e); } catch (AnzoException e) { if (log.isDebugEnabled()) { log.debug(LogUtils.DATASOURCE_MARKER, "Anzo Exception populating dataset tables", e); } throw new GlitterRuntimeException(e); } return new long[] { validNamedGraphsInDefaultGraph, validNamedGraphsInNamedGraphs }; } protected Map<URI, Long> resolveSet(CompositeNodeLayout layout, Connection connection, Set<URI> uris, boolean bypassAcls) throws AnzoException, UnknownGraphException { Map<URI, Long> graphIds = layout.getNodeURILayout().resolveStoredNodes(uris, false, connection, -1); if (!bypassAcls && graphIds.size() < uris.size()) { uris.removeAll(graphIds.keySet()); throw new UnknownGraphException(StringUtils.join(uris.iterator(), ", ")); } return graphIds; } /** * <ol> * <li>Everything in graphIds needs to be checked for validity (existence, timeliness, authority), and an error thrown if anything fails * <li>If includeAllNamedGraphs && includeAllMetadataGraphs, then everything can just be dumped to destinationTable directly, after the validity check of * graphIds * <li>If only 1 of graphIds, datasetIds, includeAllNamedGraphs, or includeAllMetadataGraphs is given, then the temporary table can also be skipped * <li>Otherwise, everything must be populated into a temporary table, and then distinct values selected into destinationTable * </ol> */ protected long populateValidGraphs(CompositeNodeLayout layout, Connection connection, Set<URI> graphsSet, boolean defaults, boolean includeAllNamedGraphs, boolean includeAllMetadataGraphs, String destinationTable) throws SQLException, GlitterException, AnzoException { long graphs = 0; boolean allGraphs = includeAllMetadataGraphs && includeAllNamedGraphs; int graphSources = (graphsSet.size() > 0 ? 1 : 0) + (includeAllNamedGraphs ? 1 : 0) + (includeAllMetadataGraphs ? 1 : 0); if (graphSources == 0) return 0; boolean needsTemporaryTable = graphSources > 1; String insertTable = needsTemporaryTable ? getTemporaryTempTable() : destinationTable; if (graphsSet.size() > 0) { insertGraphsIfValid(graphsSet, insertTable, defaults); graphs += graphsSet.size(); } if (allGraphs) { graphs = insertAllGraphsIfValid(insertTable, defaults); setGraphsType(defaults, GraphSetType.ALL_GRAPHS); } else { if (graphs > 0) { setGraphsType(defaults, GraphSetType.LISTED); } if (includeAllNamedGraphs) { if (graphs == 0) { setGraphsType(defaults, GraphSetType.ALL_NAMED_GRAPHS); } graphs += insertAllNamedGraphsIfValid(insertTable, defaults); } if (includeAllMetadataGraphs) { if (graphs == 0) { setGraphsType(defaults, GraphSetType.ALL_METADATA_GRAPHS); } graphs += insertAllMetadataGraphsIfValid(insertTable, defaults); } } if (needsTemporaryTable && graphs > 0) return GlitterSQL.copyDistinctDatasetIds(getPreparedStatementProvider(), getConnection(), getSessionPrefix(), insertTable, destinationTable); return graphs; } protected boolean containsNodeType(TreeNode parent, Class<?>[] cs) { for (TreeNode child : parent.getChildren()) { if (child == null) continue; for (Class<?> element : cs) if (element.isInstance(child)) return true; if (containsNodeType(child, cs)) return true; } return false; } protected QueryOptimizer cqo = new QueryOptimizer(); boolean handleOptional(AnzoBGPQuery query, TreeNode node) throws NoSolutionsException { if (!getConfiguration().getSupportsOptionalJoins()) { return false; } Optional opt = (Optional) node; if (opt.getFilters() != null && opt.getFilters().size() > 0) return false; GraphPattern gp = opt.getMustMatchPattern(); if (gp instanceof BGP) { BGP bgp = (BGP) gp; if (!handleBGP(query, bgp)) return false; } else if (gp instanceof TriplePatternNode) { query.addTriplePattern(((TriplePatternNode) gp).getTriplePattern()); } else if (gp instanceof Optional) { if (!handleOptional(query, gp)) return false; } else if (gp instanceof Group) { if (((Group) gp).getPatterns().size() == 1) { GraphPattern graphPattern = (((Group) gp).getPatterns().iterator().next()); if (!handleGraphPattern(query, graphPattern)) return false; handleFilters(query, gp); } else { return false; } } else { return false; } GraphPattern gpMay = opt.getMayMatchPattern(); if (gpMay instanceof Group) { Group group = (Group) gpMay; for (GraphPattern gpMay2 : group.getChildren()) { if (gpMay2 instanceof BGP) { BGP bgp = (BGP) gpMay2; List<TriplePattern> patterns = new ArrayList<TriplePattern>(); for (TriplePatternNode tp : bgp.getTriplePatterns()) { patterns.add(tp.getTriplePattern()); } query.addOptionalPatterns(patterns); } else if (gpMay2 instanceof TriplePatternNode) { query.addOptionalPattern(((TriplePatternNode) gpMay2).getTriplePattern()); } else { return false; } } } else if (gpMay instanceof BGP) { BGP bgp = (BGP) gpMay; List<TriplePattern> patterns = new ArrayList<TriplePattern>(); for (TriplePatternNode tp : bgp.getTriplePatterns()) { patterns.add(tp.getTriplePattern()); } query.addOptionalPatterns(patterns); } else if (gpMay instanceof TriplePatternNode) { query.addOptionalPattern(((TriplePatternNode) gpMay).getTriplePattern()); } else { return false; } handleFilters(query, node); return true; } protected boolean handleBGP(AnzoBGPQuery query, TreeNode node) throws NoSolutionsException { BGP bgp = (BGP) node; TriplePattern ftp = null; if (bgp.getFunctionalPredicate() != null) { FunctionalPredicate fp = bgp.getFunctionalPredicate(); if (fp instanceof TextLikePredicate) { query.addLikeMatch(((TextLikePredicate) fp).var, ((TextLikePredicate) fp).textMatch); ftp = fp.getFunctionalTriplePattern(); } else { return false; } } List<TriplePatternNode> nodes = new ArrayList<TriplePatternNode>(); for (TriplePatternNode tpn : bgp.getTriplePatterns()) { if (ftp == null || !ftp.equals(tpn.getTriplePattern())) nodes.add(tpn); } for (TripleNode noder : cqo.getOrderedSet(nodes.iterator())) { if (noder.getUnMatchedVariableCount() > 0 && noder.getMatchedVariableCount() == 1) { TriplePattern tp = noder.getTriple().getTriplePattern(); query.addExtraTriplePattern(tp.getSubject(), tp.getPredicate(), tp.getObject()); } else { query.addTriplePattern(noder.getTriple().getTriplePattern()); } } handleFilters(query, node); return true; } protected boolean handleGraphPattern(AnzoBGPQuery query, TreeNode node) throws NoSolutionsException { if (node instanceof TriplePatternNode) { query.addTriplePattern(((TriplePatternNode) node).getTriplePattern()); } else if (node instanceof BGP) { if (!handleBGP(query, node)) return false; } else if (node instanceof Optional) { if (!handleOptional(query, node)) return false; } else if (node instanceof Group) { Group group = (Group) node; List<GraphPattern> children = group.getChildren(); GraphPattern gp = null; if (!children.isEmpty()) gp = children.get(0); if (gp == null || children.size() > 1) return false; if (gp instanceof BGP) { if (!handleBGP(query, gp)) return false; } else if (gp instanceof Optional) { if (!handleOptional(query, gp)) return false; } else { return false; } } else { return false; } handleFilters(query, node); return true; } protected boolean buildQuery(AnzoBGPQuery query, TreeNode node, TreeNode mustMatchNode) throws NoSolutionsException { if (!handleGraphPattern(query, node)) return false; query.catalogTriples(); return true; } protected void handleFilters(AnzoBGPQuery query, TreeNode node) { Set<Expression> removed = new HashSet<Expression>(); for (Expression filter : node.getFilters()) { handleFilter(filter, query, node, false, removed); } for (Expression filter : removed) { node.getFilters().remove(filter); } if (removed.size() > 0) { node.invalidateCache(); } } protected void handleFilter(Expression filter, AnzoBGPQuery query, TreeNode node, boolean not, Set<Expression> removed) { if (filter instanceof FunctionCall) { FunctionCall fc = ((FunctionCall) filter); if (fc.getFunction() instanceof BinaryFunction) { if (fc.getArguments().size() == 2) { Expression e1 = fc.getArguments().get(0); Expression e2 = fc.getArguments().get(1); if (e1 instanceof SimpleExpression && e2 instanceof SimpleExpression) { TriplePatternComponent v1 = ((SimpleExpression) e1).getTerm(); TriplePatternComponent v2 = ((SimpleExpression) e2).getTerm(); if (v1 instanceof Bindable && v2 instanceof Bindable) { if (fc.getFunction() instanceof PolymorphicEq) { removed.add(filter); if (not) { query.addNotEqualVariables((Bindable) v1, (Bindable) v2); } else { query.addEqualVariables((Bindable) v1, (Bindable) v2); } } else if (fc.getFunction() instanceof SameTerm) { removed.add(filter); if (not) { query.addNotEqualVariables((Bindable) v1, (Bindable) v2); } else { query.addEqualVariables((Bindable) v1, (Bindable) v2); } } else if (fc.getFunction() instanceof PolymorphicNe) { removed.add(filter); if (not) { query.addEqualVariables((Bindable) v1, (Bindable) v2); } else { query.addNotEqualVariables((Bindable) v1, (Bindable) v2); } } } } } } else if (fc.getFunction() instanceof UnaryFunction) { if (fc.getArguments().size() == 1) { Expression e1 = fc.getArguments().get(0); if (fc.getFunction() instanceof Not) { if (e1 instanceof FunctionCall) { handleFilter(e1, query, node, true, removed); } } else { if (e1 instanceof SimpleExpression) { TriplePatternComponent v1 = ((SimpleExpression) e1).getTerm(); if (v1 instanceof Bindable) { if (fc.getFunction() instanceof IsIRI) { removed.add(filter); query.addIsIRI((Bindable) v1, not); } else if (fc.getFunction() instanceof IsLiteral) { removed.add(filter); query.addIsLiteral((Bindable) v1, not); } else if (fc.getFunction() instanceof IsBlank) { removed.add(filter); query.addIsBlank((Bindable) v1, not); } } } } } } else if (fc.getFunction() instanceof LogicalAnd) { if (canHandleAllFunctions(fc.getArguments())) { for (Expression exp : fc.getArguments()) { handleFilter(exp, query, node, not, removed); } } } } } private boolean canHandleAllFunctions(List<Expression> filters) { for (Expression exp : filters) { if (exp instanceof FunctionCall) { FunctionCall fc = (FunctionCall) exp; if (!((fc.getFunction() instanceof LogicalAnd && canHandleAllFunctions(fc.getArguments())) || fc.getFunction() instanceof PolymorphicEq || fc.getFunction() instanceof PolymorphicNe || fc.getFunction() instanceof SameTerm || fc.getFunction() instanceof IsIRI || fc.getFunction() instanceof IsLiteral || fc.getFunction() instanceof IsBlank)) { return false; } } } return true; } protected SolutionSet getBindingsForQuery(AnzoBGPQuery query, TreeNode node, SolutionSet solutions, QueryController controller) throws SQLException, AnzoException { boolean isEnabled = RequestAnalysis.getAnalysisLogger().isDebugEnabled(); if (solutions == null) solutions = new SolutionList(); // The query optimizer pulls out "extra triples" - triples that are simple property // extractions from resources that are identified in other ways if (!buildQuery(query, node, null)) return null; if (isEnabled) { StringBuilder sb = new StringBuilder(); node.prettyPrint(sb, true); RequestAnalysis.getAnalysisLogger().debug("[glitter_AnzoSolutionGeneratorBase_startSolvingNode] [{}] = [{}]", node.toString(), sb.toString()); } String sql = query.getSQL(query.getExtraTriples().size() > 0 || query.getOptionalTriples().size() > 0 ? getSessionPrefix() + "TEMP_COLUMNS" : null); long start = System.currentTimeMillis(); Statement stmt = getConnection().createStatement(); try { logSql(sql, new String[] {}); ResultSet rs = null; if (query.getExtraTriples().size() > 0 || query.getOptionalTriples().size() > 0) { long start2 = 0; if (isEnabled) { start2 = System.currentTimeMillis(); } int counter = 0; try { counter = (sql != null) ? stmt.executeUpdate(sql) : 1; } catch (SQLException sqle) { log.error(LogUtils.RDB_MARKER, "Error executing query:" + sql, sqle); throw sqle; } if (controller.isCancelled()) { throw new GlitterException(ExceptionConstants.GLITTER.QUERY_CANCELLED); } if (counter > 0) { if (sql != null && isEnabled) { RequestAnalysis.getAnalysisLogger().debug("[glitter_AnzoSolutionGeneratorBase_" + "sqlInterimQuery] {}:{}:{}", new Object[] { sql, Long.toString(counter), Long.toString(System.currentTimeMillis() - start2) }); start2 = System.currentTimeMillis(); } String sqlExtra = query.getExtraSQL(); if (sql == null && sqlExtra == null) { return SolutionUtils.noSolutions(); } try { rs = stmt.executeQuery(sqlExtra); } catch (SQLException sqle) { log.error(LogUtils.RDB_MARKER, "Error executing query:" + sqlExtra, sqle); throw sqle; } if (controller.isCancelled()) { throw new GlitterException(ExceptionConstants.GLITTER.QUERY_CANCELLED); } if (isEnabled) { RequestAnalysis.getAnalysisLogger().debug("[glitter_AnzoSolutionGeneratorBase_" + "sqlExtrasQuery] {}::{}", new Object[] { sqlExtra, Long.toString(System.currentTimeMillis() - start2) }); start2 = System.currentTimeMillis(); } } } else { long start2 = 0; if (isEnabled) { start2 = System.currentTimeMillis(); } if (sql == null) { return SolutionUtils.noSolutions(); } try { rs = stmt.executeQuery(sql); } catch (SQLException sqle) { log.error(LogUtils.RDB_MARKER, "Error executing query:" + sql, sqle); throw sqle; } if (controller.isCancelled()) { throw new GlitterException(ExceptionConstants.GLITTER.QUERY_CANCELLED); } if (isEnabled) { RequestAnalysis.getAnalysisLogger().debug("[glitter_AnzoSolutionGeneratorBase_" + "sqlQuery] {}::{}", sql, Long.toString(System.currentTimeMillis() - start2)); start2 = System.currentTimeMillis(); } } int count = 0; if (rs != null) { count = addBindings(query, node, rs, solutions, SQLQueryConstants.glitterIgnoredVariable, getConnection(), controller); } if (isEnabled) { RequestAnalysis.getAnalysisLogger().debug("[glitter_AnzoSolutionGeneratorBase_" + "sqlTotalResults] {}:{}", Long.toString(count), Long.toString(System.currentTimeMillis() - start)); } return solutions; } finally { try { clearTempTable(); } catch (SQLException sqle) { getLogger().error("SQL exception clearing temporary tables", sqle); } catch (RdbException sqle) { getLogger().error("SQL exception clearing temporary tables", sqle); } if (stmt != null) stmt.close(); } } /** * * We need to translate this result set into our own bindings. This means: 1) converting column names to Bindable objects 2) ignoring the fake unit column * 3) mapping back from Anzo IDs to BNode Nodes to Glitter objects 4) associating the appropriate openrdf object with the bindable key * */ protected int addBindings(AnzoBGPQuery query, TreeNode node, ResultSet rs, SolutionSet bindings, String glitterIgnoredVariable, Connection connection, QueryController controller) throws SQLException, AnzoException { int resultsProcessed = 0; try { ResultSetMetaData meta = rs.getMetaData(); // HashMap<Integer, Bindable> columns = new HashMap<Integer, Bindable>(); int lastColumn = meta.getColumnCount(); Bindable columns[] = new Bindable[lastColumn + 1]; for (int i = 1; i <= lastColumn; i++) { String name = meta.getColumnLabel(i); if (!name.equals(glitterIgnoredVariable)) { // 2) columns[i] = query.getBindableForAlias(name); // 1) } } // TODO we'd like to filter here if possible, but it breaks if we're inside a LeftJoin that has // filters... while (rs.next()) { if (controller.isCancelled()) { throw new GlitterException(ExceptionConstants.GLITTER.QUERY_CANCELLED); } resultsProcessed++; PatternSolutionImpl solution = new PatternSolutionImpl(); for (int j = 1; j < columns.length; j++) { if (columns[j] != null) { long id = rs.getLong(j); if (id != 0) { org.openanzo.rdf.Value value = this.nodeConverter.getGlitterNode(id, connection); // 3) solution.setBinding(columns[j], value); } } } bindings.add(solution); } this.nodeConverter.resolveNodes(connection); } finally { if (rs != null) { rs.close(); } } return resultsProcessed; } protected boolean isNonVacuous(TreeNode node) { if (node == null) return false; // a node is non-vaccuous if it is a triple pattern, has filters, or has a non-vaccuous child if (node instanceof TriplePatternNode) return true; Set<Expression> filters = node.getFilters(); if (filters != null && !filters.isEmpty()) return true; for (TreeNode child : node.getChildren()) { if (isNonVacuous(child)) return true; } return false; } protected void logSql(String sql, String[] params) { String s = "SQL: " + sql + "\n\t["; for (int i = 0; i < params.length; i++) { s += params[i]; if (i + 1 < params.length) s += ", "; } s += "]"; } protected SolutionSet unitSolution() { SolutionList unit = new SolutionList(); PatternSolutionImpl emptySolution = new PatternSolutionImpl(); unit.add(emptySolution); return unit; } public boolean willHandleAssignments(MultiMap<Variable, Expression> assignments) { return false; } }