Skip to content

Commit

Permalink
Remove LogicalPlanId and use the new Id instead.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lojjs committed Dec 22, 2017
1 parent 12f9220 commit 3160a5a
Show file tree
Hide file tree
Showing 149 changed files with 696 additions and 843 deletions.
Expand Up @@ -20,7 +20,7 @@
package org.neo4j.cypher.internal.v3_4.logical.plans

import org.neo4j.cypher.internal.ir.v3_4.{CardinalityEstimation, IdName, PlannerQuery}
import org.neo4j.cypher.internal.util.v3_4.attribution.IdGen
import org.neo4j.cypher.internal.util.v3_4.attribution.{IdGen, SameId}
import org.neo4j.cypher.internal.util.v3_4.symbols._

/**
Expand All @@ -32,20 +32,20 @@ case class Argument(argumentIds: Set[IdName] = Set.empty)(val solved: PlannerQue
def availableSymbols: Set[IdName] = argumentIds

override def updateSolved(newSolved: PlannerQuery with CardinalityEstimation): Argument = {
val resultingPlan = copy(argumentIds)(newSolved)
val resultingPlan = copy(argumentIds)(newSolved)(SameId(this.id))
resultingPlan.readTransactionLayer.copyFrom(readTransactionLayer)
resultingPlan
}

override def copyPlan(): LogicalPlan = {
val resultingPlan = this.copy(argumentIds)(solved).asInstanceOf[this.type]
val resultingPlan = this.copy(argumentIds)(solved)(SameId(this.id)).asInstanceOf[this.type]
resultingPlan.readTransactionLayer.copyFrom(readTransactionLayer)
resultingPlan
}

override def dup(children: Seq[AnyRef]) = children.size match {
case 1 =>
val resultingPlan = copy(children.head.asInstanceOf[Set[IdName]])(solved).asInstanceOf[this.type]
val resultingPlan = copy(children.head.asInstanceOf[Set[IdName]])(solved)(SameId(this.id)).asInstanceOf[this.type]
resultingPlan.readTransactionLayer.copyFrom(readTransactionLayer)
resultingPlan
}
Expand Down
Expand Up @@ -22,10 +22,10 @@ package org.neo4j.cypher.internal.v3_4.logical.plans
import java.lang.reflect.Method

import org.neo4j.cypher.internal.util.v3_4.Foldable._
import org.neo4j.cypher.internal.util.v3_4.{Foldable, InternalException, Unchangeable}
import org.neo4j.cypher.internal.util.v3_4.{Foldable, InternalException, Rewritable, Unchangeable}
import org.neo4j.cypher.internal.util.v3_4.Rewritable._
import org.neo4j.cypher.internal.ir.v3_4.{CardinalityEstimation, IdName, PlannerQuery, Strictness}
import org.neo4j.cypher.internal.util.v3_4.attribution.IdGen
import org.neo4j.cypher.internal.util.v3_4.attribution.{IdGen, SameId}
import org.neo4j.cypher.internal.v3_4.expressions.Expression

object LogicalPlan {
Expand All @@ -42,7 +42,7 @@ abstract class LogicalPlan(idGen: IdGen)
extends Product
with Foldable
with Strictness
with RewritableWithMemory {
with Rewritable {

self =>

Expand All @@ -56,44 +56,14 @@ abstract class LogicalPlan(idGen: IdGen)
val readTransactionLayer: Unchangeable[Int] = new Unchangeable[Int]

val id = idGen.id()
// --------
/*
A id for the logical plan operator, unique inside of the given query tree. These identifiers will be
copied to a rewritten version of the logical plan, as long as there is a one-to-one mapping between
rewritten plans. In other words - once ids have been assigned, plan rewriting should not collapse multiple
operators into one, or split a single one into multiple new ones.
*/
def assignedId: LogicalPlanId = _id.getOrElse(throw new InternalException("Plan has not had an id assigned yet"))

def assignIds(): Unit = {
if (_id.nonEmpty)
throw new InternalException("Id has already been assigned")

var count = 0
val plans = this.findByAllClass[LogicalPlan]
plans.foreach { lp =>
lp._id = Some(new LogicalPlanId(count))
count = count + 1
}
assignedId
}

private var _id: Option[LogicalPlanId] = None

override def rememberMe(old: AnyRef): Unit = _id = old.asInstanceOf[LogicalPlan]._id

// This should only be used for converting old versions of LogicalPlans
def setIdTo(id: LogicalPlanId) : Unit = _id = Some(id)
// ^ TODO DIE
// ---------

def leaves: Seq[LogicalPlan] = this.treeFold(Seq.empty[LogicalPlan]) {
case plan: LogicalPlan
if plan.lhs.isEmpty && plan.rhs.isEmpty => acc => (acc :+ plan, Some(identity))
}

def updateSolved(newSolved: PlannerQuery with CardinalityEstimation): LogicalPlan = {
val arguments = this.children.toList :+ newSolved :+ idGen
val arguments = this.children.toList :+ newSolved :+ SameId(this.id)
try {
val resultingPlan = copyConstructor.invoke(this, arguments: _*).asInstanceOf[this.type]
resultingPlan.readTransactionLayer.copyFrom(readTransactionLayer)
Expand All @@ -106,7 +76,7 @@ abstract class LogicalPlan(idGen: IdGen)

def copyPlan(): LogicalPlan = {
try {
val arguments = this.children.toList :+ solved :+ idGen
val arguments = this.children.toList :+ solved :+ SameId(this.id)
val resultingPlan = copyConstructor.invoke(this, arguments: _*).asInstanceOf[this.type]
resultingPlan.readTransactionLayer.copyFrom(readTransactionLayer)
resultingPlan
Expand All @@ -131,11 +101,11 @@ abstract class LogicalPlan(idGen: IdGen)
val resultingPlan =
if (params.length == args.length + 1
&& params.last.isAssignableFrom(classOf[IdGen]))
constructor.invoke(this, args :+ this.idGen: _*).asInstanceOf[this.type]
constructor.invoke(this, args :+ SameId(this.id): _*).asInstanceOf[this.type]
else if ((params.length == args.length + 2)
&& params(params.length - 2).isAssignableFrom(classOf[PlannerQuery])
&& params(params.length - 1).isAssignableFrom(classOf[IdGen]))
constructor.invoke(this, args :+ this.solved :+ this.idGen: _*).asInstanceOf[this.type]
constructor.invoke(this, args :+ this.solved :+ SameId(this.id): _*).asInstanceOf[this.type]
else
constructor.invoke(this, args: _*).asInstanceOf[this.type]
resultingPlan.readTransactionLayer.copyFrom(readTransactionLayer)
Expand Down Expand Up @@ -230,19 +200,3 @@ final case class SchemaIndexSeekUsage(identifier: String, labelId : Int, label:
final case class SchemaIndexScanUsage(identifier: String, labelId : Int, label: String, propertyKey: String) extends IndexUsage
final case class ExplicitNodeIndexUsage(identifier: String, index: String) extends IndexUsage
final case class ExplicitRelationshipIndexUsage(identifier: String, index: String) extends IndexUsage

object LogicalPlanId {
// This is probably a safe way of assigning ids, but should only be used in tests
private var counter = 0
def DEFAULT: LogicalPlanId = {
val id = new LogicalPlanId(counter)
counter += 1
id
}
}

class LogicalPlanId(val underlying: Int) extends AnyVal {
def ++ : LogicalPlanId = new LogicalPlanId(underlying + 1)

override def toString: String = s"id:$underlying"
}
Expand Up @@ -42,7 +42,7 @@ case object ProcedureCallOrSchemaCommandPlanBuilder extends Phase[CompilerContex
override def postConditions: Set[Condition] = Set.empty

override def process(from: BaseState, context: CompilerContext): LogicalPlanState = {
implicit val idGen = SequentialIdGen
implicit val idGen = new SequentialIdGen()
val maybeLogicalPlan: Option[LogicalPlan] = from.statement() match {
// Global call: CALL foo.bar.baz("arg1", 2)
case Query(None, SingleQuery(Seq(resolved@ResolvedCall(signature, args, _, _, _)))) =>
Expand Down
Expand Up @@ -54,14 +54,13 @@ object DebugPrinter extends Phase[CompilerContext, LogicalPlanState, LogicalPlan
}

private def stringToLogicalPlan(string: String): (LogicalPlan, Statement) = {
implicit val idGen = SequentialIdGen
implicit val idGen = new SequentialIdGen()
val pos = InputPosition(0, 0, 0)
val solved = CardinalityEstimation.lift(RegularPlannerQuery(QueryGraph.empty), 0.0)
val stringValues = string.split("\n").map(s => StringLiteral(s)(pos))
val expression = ListLiteral(stringValues.toSeq)(pos)
val unwind = UnwindCollection(Argument(Set.empty)(solved), IdName("col"), expression)(solved)
val logicalPlan = ProduceResult(unwind, Seq("col"))
logicalPlan.assignIds()

val variable = Variable("col")(pos)
val returnItem = AliasedReturnItem(variable, variable)(pos)
Expand Down
Expand Up @@ -58,7 +58,6 @@ trait LogicalPlanRewriter extends Phase[CompilerContext, LogicalPlanState, Logic

override def process(from: LogicalPlanState, context: CompilerContext): LogicalPlanState = {
val rewritten = from.logicalPlan.endoRewrite(instance(context))
rewritten.assignIds() // This should be the only place where ids are assigned.
from.copy(maybeLogicalPlan = Some(rewritten))
}
}
Expand Up @@ -38,7 +38,7 @@ import org.neo4j.cypher.internal.v3_4.logical.plans.{DeleteExpression => DeleteE
*/
case class LogicalPlanProducer(cardinalityModel: CardinalityModel, readTransactionLayer: Int) extends ListSupport {

implicit val idGen = SequentialIdGen
implicit val idGen = new SequentialIdGen()

def withNextTxLayer: LogicalPlanProducer = copy(readTransactionLayer = this.readTransactionLayer + 1)

Expand Down
Expand Up @@ -26,7 +26,7 @@ import org.neo4j.cypher.internal.util.v3_4.attribution.SequentialIdGen
import scala.language.implicitConversions

trait LogicalPlanConstructionTestSupport extends CypherTestSupport {
implicit val idGen = SequentialIdGen
implicit val idGen = new SequentialIdGen()
implicit protected def idName(name: String): IdName = IdName(name)
implicit protected def idSymbol(name: Symbol): IdName = IdName(name.name)
}
Expand Up @@ -33,7 +33,7 @@ import org.neo4j.cypher.internal.v3_4.logical.plans._

class PlanEventHorizonTest extends CypherFunSuite {

implicit val idGen = SequentialIdGen
implicit val idGen = new SequentialIdGen()

val pos = DummyPosition(1)
val context = LogicalPlanningContext(mock[PlanContext], LogicalPlanProducer(mock[Metrics.CardinalityModel], LogicalPlan.LOWEST_TX_LAYER),
Expand Down
Expand Up @@ -26,26 +26,24 @@ import org.neo4j.cypher.internal.util.v3_4.{CypherException, Rewriter, topDown}
import org.neo4j.cypher.internal.v3_4.logical.plans.{NestedPlanExpression, _}

class LogicalPlanAssignedIdTest extends CypherFunSuite with LogicalPlanningTestSupport2 {
test("assignedId survives rewriting") {
test("id survives rewriting") {
val sr1 = Argument()(solved)
val pr = Projection(sr1, Map.empty)(solved)
pr.assignIds()

val prPrim = pr.endoRewrite(topDown(Rewriter.lift {
case sr: Argument => Argument()(solved)
}))

pr.assignedId should equal(prPrim.assignedId)
pr.lhs.get.assignedId should equal(prPrim.lhs.get.assignedId)
pr.id should equal(prPrim.id)
pr.lhs.get.id should equal(prPrim.lhs.get.id)
}

test("assignedIds are different between plans") {
test("ids are different between plans") {
val sr1 = Argument()(solved)
val sr2 = Argument()(solved)
val apply = Apply(sr1, sr2)(solved)
apply.assignIds()

sr1.assignedId shouldNot equal(sr2.assignedId)
sr1.id shouldNot equal(sr2.id)
}

test("tree structure is assigned ids in a predictable way") {
Expand All @@ -62,32 +60,12 @@ class LogicalPlanAssignedIdTest extends CypherFunSuite with LogicalPlanningTestS
val applyB = Apply(sr1B, sr2B)(solved)
val applyAll = Apply(applyA, applyB)(solved) // I heard you guys like Apply, so we applied an Apply in your Apply

applyAll.assignIds()

applyAll.assignedId.underlying should equal(0)
applyA.assignedId.underlying should equal(1)
sr1A.assignedId.underlying should equal(2)
sr2A.assignedId.underlying should equal(3)
applyB.assignedId.underlying should equal(4)
sr1B.assignedId.underlying should equal(5)
sr2B.assignedId.underlying should equal(6)
}

test("cant assign ids twice") {
val sr1 = Argument()(solved)
val pr = Projection(sr1, Map.empty)(solved)
pr.assignIds()
intercept[CypherException](pr.assignIds())
applyAll.id.x should equal(0)
applyA.id.x should equal(1)
sr1A.id.x should equal(2)
sr2A.id.x should equal(3)
applyB.id.x should equal(4)
sr1B.id.x should equal(5)
sr2B.id.x should equal(6)
}

test("can assign inside expressions as well") {
val argument = Argument()(solved)
val inner = AllNodesScan(IdName("x"), Set.empty)(solved)
val filter = Selection(Seq(NestedPlanExpression(inner, literalInt(42))(pos)), argument)(solved)

filter.assignIds()

val x = inner.assignedId // should not fail
}

}
Expand Up @@ -34,8 +34,8 @@ import org.neo4j.cypher.internal.runtime.planDescription.InternalPlanDescription
import org.neo4j.cypher.internal.runtime.planDescription.InternalPlanDescription.Arguments._
import org.neo4j.cypher.internal.runtime.planDescription.{Children, NoChildren, PlanDescriptionImpl, SingleChild, TwoChildren, Argument => Argument3_4, InternalPlanDescription => InternalPlanDescription3_4}
import org.neo4j.cypher.internal.runtime.{QueryStatistics, SCHEMA_WRITE, ExecutionMode => ExecutionModeV3_4, _}
import org.neo4j.cypher.internal.util.v3_4.attribution.Id
import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection.{BOTH, INCOMING, OUTGOING}
import org.neo4j.cypher.internal.v3_4.logical.plans.LogicalPlanId
import org.neo4j.cypher.result.QueryResult
import org.neo4j.cypher.result.QueryResult.Record
import org.neo4j.graphdb.Result.ResultVisitor
Expand Down Expand Up @@ -194,7 +194,7 @@ class ExecutionResultWrapper(val inner: InternalExecutionResult, val planner: Pl
case planDescriptionv2_3.InternalPlanDescription.Arguments.SourceCode(className, sourceCode) =>
Arguments.SourceCode(className, sourceCode)
}
PlanDescriptionImpl(LogicalPlanId.DEFAULT, name, children, arguments, planDescription.identifiers)
PlanDescriptionImpl(Id.INVALID_ID, name, children, arguments, planDescription.identifiers)
}

override def hasNext: Boolean = inner.hasNext
Expand Down
Expand Up @@ -36,9 +36,10 @@ import org.neo4j.cypher.internal.runtime.planDescription.InternalPlanDescription
import org.neo4j.cypher.internal.runtime.planDescription.InternalPlanDescription.Arguments._
import org.neo4j.cypher.internal.runtime.planDescription.{Argument, Children, NoChildren, PlanDescriptionImpl, SingleChild, TwoChildren, InternalPlanDescription => InternalPlanDescription3_4}
import org.neo4j.cypher.internal.runtime.{ExplainMode, NormalMode, ProfileMode, QueryStatistics}
import org.neo4j.cypher.internal.util.v3_4.attribution.Id
import org.neo4j.cypher.internal.util.v3_4.{symbols => symbolsv3_4}
import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection.{BOTH, INCOMING, OUTGOING}
import org.neo4j.cypher.internal.v3_4.logical.plans.{LogicalPlanId, QualifiedName}
import org.neo4j.cypher.internal.v3_4.logical.plans.QualifiedName
import org.neo4j.cypher.result.QueryResult
import org.neo4j.cypher.result.QueryResult.Record
import org.neo4j.graphdb
Expand Down Expand Up @@ -145,7 +146,7 @@ class ExecutionResultWrapper(val inner: InternalExecutionResult, val planner: Pl
val procName = QualifiedName(procedureName.namespace, procedureName.name)
Arguments.Signature(procName, Seq.empty, results.map(pair => (pair._1, lift(pair._2))))
}
PlanDescriptionImpl(LogicalPlanId.DEFAULT, name, children, arguments, planDescription.variables)
PlanDescriptionImpl(Id.INVALID_ID, name, children, arguments, planDescription.variables)
}

private def lift(cypherType: symbols3_1.CypherType): symbolsv3_4.CypherType = cypherType match {
Expand Down
Expand Up @@ -38,7 +38,7 @@ import org.neo4j.cypher.internal.v3_4.logical.plans.{LogicalPlan => LogicalPlanV
import org.neo4j.cypher.internal.v3_4.logical.{plans => plansV3_4}
import org.neo4j.cypher.internal.v3_4.{expressions => expressionsV3_4}
import org.neo4j.cypher.internal.compiler.{v3_3 => compilerV3_3}
import org.neo4j.cypher.internal.util.v3_4.attribution.{IdGen, SequentialIdGen}
import org.neo4j.cypher.internal.util.v3_4.attribution.{Id, IdGen, SameId, SequentialIdGen}

import scala.collection.mutable
import scala.collection.mutable.{HashMap => MutableHashMap}
Expand All @@ -55,27 +55,25 @@ object LogicalPlanConverter {
override def apply(v1: (AnyRef, Seq[AnyRef])): AnyRef = rewriter.apply(v1)

private val rewriter: RewriterWithArgs = bottomUpWithArgs { before =>
implicit val idGen = SequentialIdGen

val rewritten = RewriterWithArgs.lift {
case (plan: plansV3_3.Argument, children: Seq[AnyRef]) =>
plansV3_4.Argument(children.head.asInstanceOf[Set[IdNameV3_4]])(new PlannerQueryWrapper(plan.solved))
plansV3_4.Argument(children.head.asInstanceOf[Set[IdNameV3_4]])(new PlannerQueryWrapper(plan.solved))(SameId(Id(plan.assignedId.underlying)))
case (plan: plansV3_3.SingleRow, _) =>
plansV3_4.Argument()(new PlannerQueryWrapper(plan.solved))
case (_: plansV3_3.ProduceResult, children: Seq[AnyRef]) =>
plansV3_4.Argument()(new PlannerQueryWrapper(plan.solved))(SameId(Id(plan.assignedId.underlying)))
case (plan: plansV3_3.ProduceResult, children: Seq[AnyRef]) =>
plansV3_4.ProduceResult(source = children(1).asInstanceOf[LogicalPlanV3_4],
columns = children(0).asInstanceOf[Seq[String]])
columns = children(0).asInstanceOf[Seq[String]])(SameId(Id(plan.assignedId.underlying)))
case (plan: plansV3_3.TriadicSelection, children: Seq[AnyRef]) =>
plansV3_4.TriadicSelection(left = children(1).asInstanceOf[LogicalPlanV3_4],
right = children(5).asInstanceOf[LogicalPlanV3_4],
positivePredicate = children(0).asInstanceOf[Boolean],
sourceId = children(2).asInstanceOf[IdNameV3_4],
seenId = children(3).asInstanceOf[IdNameV3_4],
targetId = children(4).asInstanceOf[IdNameV3_4])(new PlannerQueryWrapper(plan.solved))
targetId = children(4).asInstanceOf[IdNameV3_4])(new PlannerQueryWrapper(plan.solved))(SameId(Id(plan.assignedId.underlying)))
case (plan: plansV3_3.ProceduralLogicalPlan, children: Seq[AnyRef]) =>
convertVersion("v3_3", "v3_4", plan, children, idGen, classOf[IdGen])
convertVersion("v3_3", "v3_4", plan, children, SameId(Id(plan.assignedId.underlying)), classOf[IdGen])
case (plan: plansV3_3.LogicalPlan, children: Seq[AnyRef]) =>
convertVersion("v3_3", "v3_4", plan, children, new PlannerQueryWrapper(plan.solved), classOf[PlannerQuery], idGen, classOf[IdGen])
convertVersion("v3_3", "v3_4", plan, children, new PlannerQueryWrapper(plan.solved), classOf[PlannerQuery], SameId(Id(plan.assignedId.underlying)), classOf[IdGen])

case (inp: astV3_3.InvalidNodePattern, children: Seq[AnyRef]) =>
new expressionsV3_4.InvalidNodePattern(children.head.asInstanceOf[Option[expressionsV3_4.Variable]].get)(helpers.as3_4(inp.position))
Expand Down Expand Up @@ -164,7 +162,6 @@ object LogicalPlanConverter {
case plan: LogicalPlanV3_3 =>
try {
val plan3_4 = rewritten.asInstanceOf[LogicalPlanV3_4]
plan3_4.setIdTo(helpers.as3_4(plan.assignedId))
// 3.3 does not know about transaction layers, it plans Eagers instead.
plan3_4.readTransactionLayer.value = 0
} catch {
Expand Down
Expand Up @@ -38,9 +38,9 @@ import org.neo4j.cypher.internal.frontend.v3_4.{phases => phasesV3_4}
import org.neo4j.cypher.internal.ir.v3_3.{Cardinality => CardinalityV3_3}
import org.neo4j.cypher.internal.ir.{v3_3 => irV3_3, v3_4 => irV3_4}
import org.neo4j.cypher.internal.planner.v3_4.spi.{DPPlannerName, IDPPlannerName, PlannerNameWithVersion, ProcedurePlannerName}
import org.neo4j.cypher.internal.util.v3_4.attribution.Id
import org.neo4j.cypher.internal.util.v3_4.{Cardinality, InputPosition}
import org.neo4j.cypher.internal.v3_3.logical.plans.{LogicalPlanId => LogicalPlanIdV3_3}
import org.neo4j.cypher.internal.v3_4.logical.plans.{LogicalPlanId => LogicalPlanIdV3_4}
import org.neo4j.kernel.impl.query.{QueryExecutionMonitor, TransactionalContext}

object helpers {
Expand Down Expand Up @@ -97,7 +97,7 @@ object helpers {

def as3_4(pos: InputPositionV3_3): InputPosition = if(pos == null) null else InputPosition(pos.offset, pos.line, pos.column)

def as3_4(planId: LogicalPlanIdV3_3) : LogicalPlanIdV3_4 = new LogicalPlanIdV3_4(planId.underlying)
def as3_4(planId: LogicalPlanIdV3_3) : Id = Id(planId.underlying)

def as3_4(plannerName: PlannerNameV3_3) : PlannerName = plannerName match {
case IDPPlannerNameV3_3 => IDPPlannerName
Expand Down

0 comments on commit 3160a5a

Please sign in to comment.