/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. 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 Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Sep 28, 2011 */ package com.bigdata.rdf.sparql.ast.eval; import java.io.Serializable; import java.util.Enumeration; import java.util.Properties; import org.apache.log4j.Logger; import org.openrdf.query.Dataset; import org.openrdf.query.algebra.StatementPattern.Scope; import com.bigdata.bop.BOp; import com.bigdata.bop.IPredicate; import com.bigdata.bop.PipelineOp; import com.bigdata.bop.cost.SubqueryCostReport; import com.bigdata.rdf.sparql.ast.ASTBase; import com.bigdata.rdf.sparql.ast.StatementPatternNode; /** * Base class provides support for triples, sids, and quads mode joins which * was refactored from the old Rule2BOpUtility class. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class AST2BOpBase { private static final Logger log = Logger.getLogger(AST2BOpBase.class); protected AST2BOpBase() { } /** * Annotations used to mark named and default graph patterns on the * {@link IPredicate}s. Rather than attaching a named or default graph * expander, we annotate the predicate with the metadata for the access path * and then convert that annotation to the appropriate pipeline operators. */ public interface Annotations { /** * Boolean flag indicates that the database is operating in quads mode. */ String QUADS = AST2BOpBase.class.getName() + ".quads"; boolean DEFAULT_QUADS = false; /** * The {@link Dataset} associated with the access path (quads mode * only). The {@link Dataset} is only provided by openrdf when FROM or * FROM NAMED was used in the query. Otherwise the {@link Dataset} will * be <code>null</code> and is not attached as an annotation. * <p> * Note: This annotation MUST be stripped from the query plan to prevent * an attempt to serialized it for RMI in scale-out (the {@link Dataset} * is not {@link Serializable}, can be quite large, and is captured by * other constructions in the generated query plan). */ String DATASET = AST2BOpBase.class.getName() + ".dataset"; /** * The {@link Scope} of the access path (quads mode only). In quads mode * the {@link Scope} is always provided by openrdf. * * @see Scope#NAMED_CONTEXTS * @see Scope#DEFAULT_CONTEXTS */ String SCOPE = AST2BOpBase.class.getName() + ".scope"; /** * Boolean annotation indicates whether the generated JOIN is simple (a * single JOIN operator with optional constraints but without any * variable materialization requirements) or complex (a JOIN operator * associated with at least one constraint which requires the * materialization of variables that are not already known to be * materialized). */ String SIMPLE_JOIN = AST2BOpBase.class.getName() + ".simpleJoin"; /* * Query planner and cost estimates. */ /** * The original index assigned to the access path by the static query * optimizer. * <p> * Note: The actual index will be chosen at runtime based on the asBound * predicate. In scale-out, the binding sets are send to the node having * the shard on which the asBound predicate would read. * * TODO It would make sense to lift this annotation into a different AST * optimizer so it is always present. An optimization for index locality * for as-bound evaluation depends on the presence of this annotation. * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/150" > * Choosing the index for testing fully bound access paths based on * index locality</a> */ String ORIGINAL_INDEX = AST2BOpBase.class.getName() + ".originalIndex"; /** * The estimated cardinality of an access path as determined during * static query optimization. This is the fast range count if the * predicate and {@link Long#MAX_VALUE} if the predicate is part of an * optional join (this is used by the query optimized to order the * optional joins to the end since they can not increase the selectivity * of the query). */ String ESTIMATED_CARDINALITY = AST2BOpBase.class.getName() + ".estimatedCardinality"; /** * The estimated cost of a SCAN + FILTER approach to a default graph or * named graph query. */ String COST_SCAN = AST2BOpBase.class.getName() + ".cost.scan"; /** * A {@link SubqueryCostReport} on the estimated cost of a SUBQUERY * approach to a default graph or named graph query. */ String COST_SUBQUERY = AST2BOpBase.class.getName() + ".cost.subquery"; /** * The #of known graphs in the {@link Dataset} for a default graph or * named graph query. */ String NKNOWN = AST2BOpBase.class.getName() + ".nknown"; /** * Boolean indicating whether the AST2BOpUpdate should autocommit. Default * value is TRUE. */ String AUTO_COMMIT = AST2BOpBase.class.getName() + "AUTO_COMMIT"; } /** * Return either <i>left</i> wrapped as the sole member of an array or * {@link BOp#NOARGS} iff <i>left</i> is <code>null</code>. * * @param left * The prior operator in the pipeline (optional). * @return The array. */ static protected BOp[] leftOrEmpty(final PipelineOp left) { return left == null ? BOp.NOARGS : new BOp[] { left }; } // /** // * Apply any query hints to the operator as annotations of that operator. // * <p> // * Note: This method is responsible for transferring query hints from // * {@link ASTBase#getQueryHints()} onto a generated {@link PipelineOp}. // * // * @param op // * The operator. // * @param queryHints // * The query hints (from {@link ASTBase#getQueryHints()}). // * // * @return A copy of that operator to which the query hints (if any) have // * been applied. If there are no query hints then the original // * operator is returned. // * // * @deprecated by // * {@link #applyQueryHints(PipelineOp, ASTBase, AST2BOpContext)} // * which allows by global and AST node specific query hints to // * be applied. // * // * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/791" > // * Clean up query hints </a> // */ // @Deprecated // protected static PipelineOp applyQueryHints(PipelineOp op, // final Properties queryHints) { // // return _applyQueryHints(op, queryHints); // // } /** * Apply any query hints to the operator as annotations of that operator. * <p> * Note: This method is responsible for transferring query hints from * {@link ASTBase#getQueryHints()} onto a generated {@link PipelineOp}. * * @param op * The pipeline operator generated from some AST node. * @param node * The AST node from which the pipeline operator was generated * (required). The query hints (from * {@link ASTBase#getQueryHints()}) will be applied to that * pipeline operator. * @param ctx * The evaluation context (ignored). Global query hints declared * here will be applied to the generated pipeline operator. * Global hints are applied <strong>first</strong> so they can be * override by AST node specific hints. * * @return A copy of that operator to which the query hints (if any) have * been applied. If there are no query hints then the original * operator is returned. * * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/791" > * Clean up query hints </a> */ protected static PipelineOp applyQueryHints(PipelineOp op, final ASTBase node, final AST2BOpContext ctx) { /* * Note: The global query hints are transferred onto the AST nodes by * the ASTQueryHintOptimizer and the registered IQueryHint classes. They * do NOT need to be reapplied here. */ // // Apply global query hints from ASTContext. // op = _applyQueryHints(op, ctx.queryHints); // if (node != null) { // Apply ASTBase node specific query hints. op = _applyQueryHints(op, node.getQueryHints()); // } return op; } /** * Apply any query hints to the operator as annotations of that operator. * <p> * Note: This method is responsible for transferring query hints from * {@link ASTBase#getQueryHints()} onto a generated {@link PipelineOp}. * <p> * Note: This alternative form is for use within caller contexts in which * (due to historical reasons) we have the query hints object for the AST * node, but not the AST node itself. This pattern shows up in the join() * methods since the {@link StatementPatternNode} is not passed through. * * @param op * The pipeline operator generated from some AST node (required). * @param nodeQueryHints * The query hints for the AST node from which the pipeline * operator was generated or its dominating operator context * since not all operators have query hints applied (required). * @param ctx * The evaluation context (ignored). Global query hints declared * here will be applied to the generated pipeline operator. * Global hints are applied <strong>first</strong> so they can be * override by AST node specific hints. * * @return A copy of that operator to which the query hints (if any) have * been applied. If there are no query hints then the original * operator is returned. * * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/791" > * Clean up query hints </a> */ protected static PipelineOp applyQueryHints(PipelineOp op, final Properties nodeQueryHints, final AST2BOpContext ctx) { /* * Note: The global query hints are transferred onto the AST nodes by * the ASTQueryHintOptimizer and the registered IQueryHint classes. They * do NOT need to be reapplied here. */ // // Apply global query hints from ASTContext. // op = _applyQueryHints(op, ctx.queryHints); // if (nodeQueryHints != null) { // Apply ASTBase node specific query hints. op = _applyQueryHints(op, nodeQueryHints); // } return op; } /** * Apply any query hints to the operator as annotations of that operator. * <p> * Note: This method is responsible for transferring query hints from * {@link ASTBase#getQueryHints()} onto a generated {@link PipelineOp}. * * @param op * The operator. * @param queryHints * The query hints (optional). * * @return A copy of that operator to which the query hints (if any) have * been applied. If there are no query hints then the original * operator is returned. */ private static PipelineOp _applyQueryHints(PipelineOp op, final Properties queryHints) { if (queryHints == null) return op; final Enumeration<?> pnames = queryHints.propertyNames(); while (pnames.hasMoreElements()) { final String name = (String) pnames.nextElement(); final String value = queryHints.getProperty(name); if (log.isDebugEnabled()) log.debug("Query hint: op=" + (op.getClass().getSimpleName()) + " [" + name + "=" + value + "]"); op = (PipelineOp) op.setProperty(name, value); } return op; } }