Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/3.1' into 3.2
Browse files Browse the repository at this point in the history
  • Loading branch information
alexaverbuch committed Jul 19, 2017
2 parents add7bd3 + d041e88 commit 3b1b524
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 5 deletions.
Expand Up @@ -22,10 +22,12 @@ package org.neo4j.cypher.internal.compiler.v3_2.planner.logical
import org.neo4j.cypher.internal.compiler.v3_2.planner.LogicalPlanningTestSupport2
import org.neo4j.cypher.internal.compiler.v3_2.planner.logical.plans.rewriter.unnestOptional
import org.neo4j.cypher.internal.compiler.v3_2.planner.logical.plans.{Limit, _}
import org.neo4j.cypher.internal.frontend.v3_2.Foldable._
import org.neo4j.cypher.internal.frontend.v3_2.SemanticDirection
import org.neo4j.cypher.internal.frontend.v3_2.ast._
import org.neo4j.cypher.internal.frontend.v3_2.test_helpers.CypherFunSuite
import org.neo4j.cypher.internal.ir.v3_2.{IdName, SimplePatternLength}
import org.neo4j.cypher.internal.ir.v3_2.{Cardinality, IdName, SimplePatternLength}
import org.neo4j.kernel.impl.util.dbstructure.DbStructureLargeOptionalMatchStructure

class OptionalMatchPlanningIntegrationTest extends CypherFunSuite with LogicalPlanningTestSupport2 {

Expand Down Expand Up @@ -149,4 +151,63 @@ class OptionalMatchPlanningIntegrationTest extends CypherFunSuite with LogicalPl
Seq(propEquality, labelCheck))(s)
)
}

test(
"should plan for large number of optional matches without numerical overflow in estimatedRows") {

val lom: LogicalPlanningEnvironment[_] = new fromDbStructure(DbStructureLargeOptionalMatchStructure.INSTANCE)
val query =
"""
|MATCH (me:Label1)-[rmeState:REL1]->(meState:Label2 {deleted: 0})
|USING INDEX meState:Label2(id)
|WHERE meState.id IN [63241]
|WITH *
|OPTIONAL MATCH(n1:Label3 {deleted: 0})<-[:REL1]-(:Label4)<-[:REL2]-(meState)
|OPTIONAL MATCH(n2:Label5 {deleted: 0})<-[:REL1]-(:Label6)<-[:REL2]-(meState)
|OPTIONAL MATCH(n3:Label7 {deleted: 0})<-[:REL1]-(:Label8)<-[:REL2]-(meState)
|OPTIONAL MATCH(n4:Label9 {deleted: 0})<-[:REL1]-(:Label10) <-[:REL2]-(meState)
|OPTIONAL MATCH p1 = (:Label2 {deleted: 0})<-[:REL1|:REL3*]-(meState)
|OPTIONAL MATCH(:Label11 {deleted: 0})<-[r1:REL1]-(:Label12) <-[:REL5]-(meState)
|OPTIONAL MATCH(:Label13 {deleted: 0})<-[r2:REL1]-(:Label14) <-[:REL6]-(meState)
|OPTIONAL MATCH(:Label15 {deleted: 0})<-[r3:REL1]-(:Label16) <-[:REL7]-(meState)
|OPTIONAL MATCH(:Label17 {deleted: 0})<-[r4:REL1]-(:Label18)<-[:REL8]-(meState)
|
|OPTIONAL MATCH(:Label19 {deleted: 0})<-[r5:REL1]-(:Label20) <-[:REL2]-(n1)
|OPTIONAL MATCH(:Label19 {deleted: 0})<-[r6:REL1]-(:Label20)<-[:REL2]-(n1)
|
|OPTIONAL MATCH(n5:Label21 {deleted: 0})<-[:REL1]-(:Label22)<-[:REL2]-(n2)
|
|OPTIONAL MATCH(n6:Label3 {deleted: 0})<-[:REL1]-(:Label4)<-[:REL2]-(n5)
|OPTIONAL MATCH(:Label19 {deleted: 0})<-[r7:REL1]-(:Label20)<-[:REL2]-(n5)
|
|OPTIONAL MATCH(:Label19 {deleted: 0})<-[r8:REL1]-(:Label20)<-[:REL2]-(n6)
|
|OPTIONAL MATCH(n7:Label23 {deleted: 0})<-[:REL1]-(:Label24)<-[:REL2]-(n3)
|
|OPTIONAL MATCH(n8:Label3 {deleted: 0})<-[:REL1]-(:Label4)<-[:REL2]-(n7)
|OPTIONAL MATCH(:Label19 {deleted: 0})<-[r9:REL1]-(:Label20)<-[:REL2]-(n7)
|
|OPTIONAL MATCH(:Label19 {deleted: 0})<-[r10:REL1]-(:Label20)<-[:REL2]-(n8)
|
|OPTIONAL MATCH(n9:Label25 {deleted: 0})<-[:REL1]-(:Label26)<-[:REL2]-(n4)
|
|OPTIONAL MATCH(n10:Label3 {deleted: 0})<-[:REL1]-(:Label4) <-[:REL2]-(n9)
|OPTIONAL MATCH(:Label19 {deleted: 0})<-[r11:REL1]-(:Label20) <-[:REL2]-(n9)
|
|OPTIONAL MATCH(:Label19 {deleted: 0})<-[r12:REL1]-(:Label20)<-[:REL2]-(n10)
|OPTIONAL MATCH (me)-[:REL4]->(:Label2:Label27)
|RETURN *
""".stripMargin

lom.getLogicalPlanFor(query)._2.treeExists {
case plan:LogicalPlan =>
plan.solved.estimatedCardinality match {
case Cardinality(amount) =>
withClue("We should not get a NaN cardinality.") {
amount.isNaN should not be true
}
}
false // this is a "trick" to use treeExists to iterate over the whole tree
}
}
}
Expand Up @@ -25,13 +25,14 @@ case class Cardinality(amount: Double) extends Ordered[Cardinality] {

def compare(that: Cardinality) = amount.compare(that.amount)
def *(that: Multiplier): Cardinality = amount * that.coefficient
def *(that: Selectivity): Cardinality = amount * that.factor
def *(that: Selectivity): Cardinality = if ( that.factor == 0 ) Cardinality.EMPTY else amount * that.factor
def +(that: Cardinality): Cardinality = amount + that.amount
def *(that: Cardinality): Cardinality = amount * that.amount
def *(that: Cardinality): Cardinality = if( amount == 0 || that.amount == 0 ) Cardinality.EMPTY
else Cardinality.noInf(amount * that.amount)
def /(that: Cardinality): Option[Selectivity] = if (that.amount == 0) None else Selectivity.of(amount / that.amount)
def *(that: CostPerRow): Cost = amount * that.cost
def *(that: Cost): Cost = amount * that.gummyBears
def ^(a: Int): Cardinality = Math.pow(amount, a)
def ^(a: Int): Cardinality = Cardinality.noInf(Math.pow(amount, a))
def map(f: Double => Double): Cardinality = f(amount)

def inverse = Multiplier(1.0d / amount)
Expand All @@ -47,6 +48,8 @@ object Cardinality {

implicit def lift(amount: Double): Cardinality = Cardinality(amount)

private def noInf(value: Double) = if( value == Double.PositiveInfinity ) Double.MaxValue else value

def min(l: Cardinality, r: Cardinality): Cardinality = Math.min(l.amount, r.amount)

def max(l: Cardinality, r: Cardinality): Cardinality = Math.max(l.amount, r.amount)
Expand Down
Expand Up @@ -37,4 +37,14 @@ class SelectivityTest extends CypherFunSuite {
Selectivity.CLOSEST_TO_ONE.negate should not equal Selectivity.ONE
}

test("selectivity and cardinality should not be able to produce NaN or Infinity through multiplication") {
( Cardinality(Double.PositiveInfinity) * Selectivity.ZERO ) should equal ( Cardinality.EMPTY )

val maxCardinality = Cardinality(Double.MaxValue)
( Cardinality(Double.PositiveInfinity) * Cardinality.SINGLE ) should equal ( maxCardinality )
( Cardinality(Double.PositiveInfinity) * Cardinality(12) ) should equal ( maxCardinality )
( maxCardinality * Cardinality(1.00001) ) should equal ( maxCardinality )

( Cardinality(3223143) ^ 50 ) should equal ( maxCardinality )
}
}
Expand Up @@ -131,7 +131,7 @@ private void formatPreamble( Appendable builder ) throws IOException
}
formatln( builder );
formatln( builder, "//" );
formatln( builder, "// GENERATED FILE. DO NOT EDIT. " );
formatln( builder, "// GENERATED FILE. DO NOT EDIT." );
formatln( builder, "//" );
formatln( builder, "// This has been generated by:" );
formatln( builder, "//" );
Expand Down
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2002-2017 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.util.dbstructure;

import org.neo4j.helpers.collection.Visitable;
import org.neo4j.kernel.api.schema.index.IndexDescriptorFactory;

//
// GENERATED FILE. DO NOT EDIT.
//
// This has been generated by:
//
// org.neo4j.kernel.impl.util.dbstructure.DbStructureTool org.neo4j.kernel.impl.util.dbstructure.DbStructureLargeOptionalMatchStructure [<output source root>] <db-dir>
//
// (using org.neo4j.kernel.impl.util.dbstructure.InvocationTracer)
//

public enum DbStructureLargeOptionalMatchStructure
implements Visitable<DbStructureVisitor>
{
INSTANCE;

public void accept( DbStructureVisitor visitor )
{
visitor.visitLabel( 2, "Label20" );
visitor.visitLabel( 3, "Label4" );
visitor.visitLabel( 4, "Label22" );
visitor.visitLabel( 5, "Label6" );
visitor.visitLabel( 7, "Label1" );
visitor.visitLabel( 8, "Label12" );
visitor.visitLabel( 9, "Label14" );
visitor.visitLabel( 10, "Label26" );
visitor.visitLabel( 11, "Label10" );
visitor.visitLabel( 12, "Label24" );
visitor.visitLabel( 13, "Label8" );
visitor.visitLabel( 14, "Label18" );
visitor.visitLabel( 15, "Label19" );
visitor.visitLabel( 16, "Label3" );
visitor.visitLabel( 17, "Label16" );
visitor.visitLabel( 18, "Label15" );
visitor.visitLabel( 19, "Label21" );
visitor.visitLabel( 20, "Label5" );
visitor.visitLabel( 22, "Label2" );
visitor.visitLabel( 23, "Label11" );
visitor.visitLabel( 24, "Label13" );
visitor.visitLabel( 25, "Label17" );
visitor.visitLabel( 26, "Label25" );
visitor.visitLabel( 27, "Label9" );
visitor.visitLabel( 28, "Label23" );
visitor.visitLabel( 29, "Label7" );
visitor.visitLabel( 31, "Label27" );
visitor.visitPropertyKey( 0, "id" );
visitor.visitPropertyKey( 27, "deleted" );
visitor.visitRelationshipType( 1, "REL1" );
visitor.visitRelationshipType( 2, "REL4" );
visitor.visitRelationshipType( 4, "REL2" );
visitor.visitRelationshipType( 5, "REL5" );
visitor.visitRelationshipType( 6, "REL8" );
visitor.visitRelationshipType( 9, "REL6" );
visitor.visitIndex( IndexDescriptorFactory.forLabel( 22, 0 ), ":Label2(id)", 0.3641877706337751d, 304838L );
visitor.visitAllNodesCount( 2668827L );
visitor.visitNodeCount( 2, "Label20", 3L );
visitor.visitNodeCount( 3, "Label4", 0L );
visitor.visitNodeCount( 4, "Label22", 0L );
visitor.visitNodeCount( 5, "Label6", 0L );
visitor.visitNodeCount( 7, "Label1", 111110L );
visitor.visitNodeCount( 8, "Label12", 111112L );
visitor.visitNodeCount( 9, "Label14", 99917L );
visitor.visitNodeCount( 10, "Label26", 3L );
visitor.visitNodeCount( 11, "Label10", 111150L );
visitor.visitNodeCount( 12, "Label24", 0L );
visitor.visitNodeCount( 13, "Label8", 0L );
visitor.visitNodeCount( 14, "Label18", 111112L );
visitor.visitNodeCount( 15, "Label19", 3L );
visitor.visitNodeCount( 16, "Label3", 0L );
visitor.visitNodeCount( 17, "Label16", 0L );
visitor.visitNodeCount( 18, "Label15", 0L );
visitor.visitNodeCount( 19, "Label21", 0L );
visitor.visitNodeCount( 20, "Label5", 0L );
visitor.visitNodeCount( 22, "Label2", 310059L );
visitor.visitNodeCount( 23, "Label11", 179015L );
visitor.visitNodeCount( 24, "Label13", 99917L );
visitor.visitNodeCount( 25, "Label17", 179021L );
visitor.visitNodeCount( 26, "Label25", 3L );
visitor.visitNodeCount( 27, "Label9", 111150L );
visitor.visitNodeCount( 28, "Label23", 0L );
visitor.visitNodeCount( 29, "Label7", 0L );
visitor.visitNodeCount( 31, "Label27", 1567352L );
visitor.visitRelCount( -1, -1, -1, "MATCH ()-[]->() RETURN count(*)", 4944492L );

}
}

/* END OF GENERATED CONTENT */

0 comments on commit 3b1b524

Please sign in to comment.