/** 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 */ package com.bigdata.rdf.sparql.ast.optimizers; import static com.bigdata.rdf.sparql.ast.optimizers.AbstractOptimizerTestCase.HelperFlag.ONE_OR_MORE; import static com.bigdata.rdf.sparql.ast.optimizers.AbstractOptimizerTestCase.HelperFlag.SUBGROUP_OF_ALP; import static com.bigdata.rdf.sparql.ast.optimizers.AbstractOptimizerTestCase.HelperFlag.ZERO_OR_MORE; import static com.bigdata.rdf.sparql.ast.optimizers.AbstractOptimizerTestCase.HelperFlag.ZERO_OR_ONE; import com.bigdata.rdf.internal.IV; import com.bigdata.rdf.sparql.ast.ArbitraryLengthPathNode; import com.bigdata.rdf.sparql.ast.GroupMemberNodeBase; import com.bigdata.rdf.sparql.ast.StatementPatternNode; import com.bigdata.rdf.sparql.ast.eval.AST2BOpContext; /** * Trac733 shows some strange behavior, this test case is intended * to explore that. * * The basic issues concerns the order of execution of arbitrary length propery * paths and other bits of the query. The observed behavior was that * adding additional braces, changing the grouping had surprising effects, * and that the default choice of order was often poor. * * With this first commit we capture the incorrect behavior. * */ public class TestALPPinTrac773 extends AbstractOptimizerTestCase { private class NotNestedHelper extends Helper { public NotNestedHelper(HelperFlag zero_or_one_to_one_or_more, String sym) { this(zero_or_one_to_one_or_more, sym, true); } public NotNestedHelper(HelperFlag zero_or_one_to_one_or_more, String sym, boolean switchOrdering) { String pattern = "c" + sym; StatementPatternNode spn1 = statementPatternNode(varNode(y), constantNode(c), varNode(x), 15431); StatementPatternNode spn2 = statementPatternNode(varNode(z), constantNode(a), varNode(w), 2054); given = select( varNode(z), where ( joinGroupNode(propertyPathNode(varNode(x),pattern, constantNode(b))), spn1, propertyPathNode(varNode(x),pattern, varNode(z)), spn2 ) ); varCount = 0; // we have to evaluate this one earlier in order to get the anonymous variable numbering // lined up. Really we should compare the result with expected wise to // the unimportance of the name of anonymous variables. ArbitraryLengthPathNode alpp1; ArbitraryLengthPathNode alpp2; if (switchOrdering) { alpp2 = alpp2(zero_or_one_to_one_or_more); alpp1 = alpp1(zero_or_one_to_one_or_more); } else { alpp1 = alpp1(zero_or_one_to_one_or_more); alpp2 = alpp2(zero_or_one_to_one_or_more); } /** * Adjusted order due to changes made in history branch. The * computation of ArbitraryLengthPathNode.getEstimatedCardinality() * has changed, causing triple patterns to have precedence over * unbounded path expressions. See * https://jira.blazegraph.com/browse/BLZG-858 / trac #733. */ final GroupMemberNodeBase<?> gmn[] = (sym.equals("?")) ? new GroupMemberNodeBase[]{alpp1, spn1, alpp2, spn2} : new GroupMemberNodeBase[]{spn2, alpp2, spn1, alpp1 }; expected = select( varNode(z), where ( gmn ) ); varCount = 0; } ArbitraryLengthPathNode alpp1(HelperFlag zero_or_one_to_one_or_more) { return arbitartyLengthPropertyPath(varNode(x), constantNode(b), zero_or_one_to_one_or_more, joinGroupNode( statementPatternNode(leftVar(), constantNode(c), rightVar(), 26), SUBGROUP_OF_ALP ) ); } ArbitraryLengthPathNode alpp2(HelperFlag zero_or_one_to_one_or_more) { return arbitartyLengthPropertyPath(varNode(x), varNode(z), zero_or_one_to_one_or_more, joinGroupNode( statementPatternNode(leftVar(), constantNode(c), rightVar(), 3135), SUBGROUP_OF_ALP ) ); } } private class NestedHelper extends Helper { public NestedHelper(HelperFlag zero_or_one_to_one_or_more, String sym) { String pattern = "d" + sym; StatementPatternNode spn1 = statementPatternNode(varNode(y), constantNode(c), varNode(x), 15431); StatementPatternNode spn2 = statementPatternNode(varNode(z), constantNode(a), varNode(w), 2054); given = select( varNode(z), where ( joinGroupNode(propertyPathNode(varNode(x),pattern, constantNode(b))), spn1, joinGroupNode(propertyPathNode(varNode(x),pattern, varNode(z))), spn2 ) ); varCount = 0; ArbitraryLengthPathNode alpp1 = arbitartyLengthPropertyPath(varNode(x), constantNode(b), zero_or_one_to_one_or_more, joinGroupNode( statementPatternNode(leftVar(), constantNode(d), rightVar(), 26), SUBGROUP_OF_ALP ) ); ArbitraryLengthPathNode alpp2 = arbitartyLengthPropertyPath(varNode(x), varNode(z), zero_or_one_to_one_or_more, joinGroupNode( statementPatternNode(leftVar(), constantNode(d), rightVar(), 3135), SUBGROUP_OF_ALP ) ); /** * Adjusted order due to changes made in history branch. The * computation of ArbitraryLengthPathNode.getEstimatedCardinality() * has changed, causing triple patterns to have precedence over * unbounded path expressions. See * https://jira.blazegraph.com/browse/BLZG-858 / trac #733. */ final GroupMemberNodeBase<?> gmn[] = (sym.equals("?")) ? new GroupMemberNodeBase[]{alpp1, spn1, alpp2, spn2} : new GroupMemberNodeBase[]{spn2, alpp2, spn1, alpp1 }; expected = select( varNode(z), where ( gmn ) ); varCount = 0; } } public TestALPPinTrac773() { } public TestALPPinTrac773(String name) { super(name); } @Override IASTOptimizer newOptimizer() { return new ASTOptimizerList( new ASTPropertyPathOptimizerInTest(), new ASTRangeCountOptimizer(){ @Override protected void estimateCardinalities( StatementPatternNode sp, final IV<?, ?> s, final IV<?, ?> p, final IV<?, ?> o, final IV<?, ?> c, final AST2BOpContext ctx, final int exogenousBindingsAdjustmentFactor) { if (o != null) sp.setProperty(Annotations.ESTIMATED_CARDINALITY, 26l); else sp.setProperty(Annotations.ESTIMATED_CARDINALITY, 3135l); } }, new ASTFlattenJoinGroupsOptimizer(), new ASTStaticJoinOptimizer()); } public void testSimpleALPP() { new Helper(){{ given = select( varNode(x), where ( joinGroupNode( propertyPathNode(varNode(x),"c*", constantNode(b)) ) ) ); expected = select( varNode(x), where ( arbitartyLengthPropertyPath(varNode(x), constantNode(b), ZERO_OR_MORE, joinGroupNode( statementPatternNode(leftVar(), constantNode(c), rightVar(), 26), SUBGROUP_OF_ALP ) ) ) ); }}.test(); } public void testNestedPartway() { new NestedHelper(ZERO_OR_MORE,"*"){{ given = select( varNode(z), where ( joinGroupNode( arbitartyLengthPropertyPath(varNode(x), constantNode(b), ZERO_OR_MORE, joinGroupNode( statementPatternNode(leftVar(), constantNode(d), rightVar(), 26) ) ) ), statementPatternNode(varNode(y), constantNode(c), varNode(x), 15431), joinGroupNode( arbitartyLengthPropertyPath(varNode(x), varNode(z), ZERO_OR_MORE, joinGroupNode( statementPatternNode(leftVar(), constantNode(d), rightVar(), 3135) ) ) ), statementPatternNode(varNode(z), constantNode(a), varNode(w), 2054) ) ); }}.test(); } public void testNotNestedPartway() { new NotNestedHelper(ZERO_OR_MORE,"*", false){{ given = select( varNode(z), where ( joinGroupNode( arbitartyLengthPropertyPath(varNode(x), constantNode(b), ZERO_OR_MORE, joinGroupNode( statementPatternNode(leftVar(), constantNode(c), rightVar(), 26) ) ) ), statementPatternNode(varNode(y), constantNode(c), varNode(x), 15431), arbitartyLengthPropertyPath(varNode(x), varNode(z), ZERO_OR_MORE, joinGroupNode( statementPatternNode(leftVar(), constantNode(c), rightVar(), 3135) ) ), statementPatternNode(varNode(z), constantNode(a), varNode(w), 2054) ) ); }}.test(); } public void testNestedStar() { new NestedHelper(ZERO_OR_MORE,"*").test(); } public void testNotNestedStar() { new NotNestedHelper(ZERO_OR_MORE,"*").test(); } public void testNestedPlus() { new NestedHelper(ONE_OR_MORE,"+").test(); } public void testNotNestedPlus() { new NotNestedHelper(ONE_OR_MORE,"+").test(); } public void testNestedQuestionMark() { new NestedHelper(ZERO_OR_ONE,"?").test(); } public void testNotNestedQuestionMark() { new NotNestedHelper(ZERO_OR_ONE,"?").test(); } }