Skip to content

Commit

Permalink
Support calling by name as well as by id
Browse files Browse the repository at this point in the history
In order to support older versions of Cypher (3.3) we need to keep the
infrastructure for calling procedures et al by name as well.
  • Loading branch information
pontusmelke committed Mar 6, 2018
1 parent 8e1a550 commit c50ae5e
Show file tree
Hide file tree
Showing 26 changed files with 647 additions and 101 deletions.
Expand Up @@ -24,13 +24,13 @@ import org.neo4j.cypher.internal.util.v3_4.symbols.CypherType
import org.neo4j.cypher.internal.v3_4.expressions.FunctionInvocation

case class ProcedureSignature(name: QualifiedName,
id: Int,
inputSignature: IndexedSeq[FieldSignature],
outputSignature: Option[IndexedSeq[FieldSignature]],
deprecationInfo: Option[String],
accessMode: ProcedureAccessMode,
description: Option[String] = None,
warning: Option[String] = None) {
warning: Option[String] = None,
id: Option[Int] = None) {

def outputFields = outputSignature.getOrElse(Seq.empty)

Expand All @@ -43,13 +43,13 @@ case class ProcedureSignature(name: QualifiedName,
}

case class UserFunctionSignature(name: QualifiedName,
id: Int,
inputSignature: IndexedSeq[FieldSignature],
outputType: CypherType,
deprecationInfo: Option[String],
allowed: Array[String],
description: Option[String],
isAggregate: Boolean) {
isAggregate: Boolean,
id: Option[Int] = None) {
override def toString = s"$name(${inputSignature.mkString(", ")}) :: ${outputType.toNeoTypeString}"
}

Expand Down
Expand Up @@ -35,7 +35,7 @@ object ProcedureDeprecationWarnings extends VisitorPhase[BaseContext, BaseState]

private def findDeprecations(statement: Statement): Set[InternalNotification] =
statement.treeFold(Set.empty[InternalNotification]) {
case f@ResolvedCall(ProcedureSignature(name, _, _, _, Some(deprecatedBy), _, _, _), _, _, _, _) =>
case f@ResolvedCall(ProcedureSignature(name, _, _, Some(deprecatedBy), _, _, _, _), _, _, _, _) =>
(seq) => (seq + DeprecatedProcedureNotification(f.position, name.toString, deprecatedBy), None)
case _:UnresolvedCall =>
throw new InternalException("Expected procedures to have been resolved already")
Expand All @@ -55,9 +55,9 @@ object ProcedureWarnings extends VisitorPhase[BaseContext, BaseState] {

private def findWarnings(statement: Statement): Set[InternalNotification] =
statement.treeFold(Set.empty[InternalNotification]) {
case f@ResolvedCall(ProcedureSignature(name, _,_, _, _, _, _, Some(warning)), _, _, _, _) =>
case f@ResolvedCall(ProcedureSignature(name, _, _, _, _, _, Some(warning),_), _, _, _, _) =>
(seq) => (seq + ProcedureWarningNotification(f.position, name.toString, warning), None)
case ResolvedCall(ProcedureSignature(name, _, _, Some(output), None, _, _, _), _, results, _, _)
case ResolvedCall(ProcedureSignature(name, _, Some(output), None, _, _, _, _), _, results, _, _)
if output.exists(_.deprecated) => (set) => (set ++ usedDeprecatedFields(name.toString, results, output), None)
case _:UnresolvedCall =>
throw new InternalException("Expected procedures to have been resolved already")
Expand Down
Expand Up @@ -34,7 +34,7 @@ class RewriteProcedureCallsTest extends CypherFunSuite with AstConstructionTestS
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))

val signature = ProcedureSignature(qualifiedName, 42, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty[String]))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty[String]))
val procLookup: (QualifiedName) => ProcedureSignature = _ => signature
val fcnLookup: (QualifiedName) => Option[UserFunctionSignature] = _ => None

Expand Down
Expand Up @@ -37,7 +37,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
val unresolved = UnresolvedCall(ns, name, None, None)(pos)
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = IndexedSeq(Parameter("a", CTAny)(pos))
val callResults = IndexedSeq(ProcedureResultItem(varFor("x"))(pos), ProcedureResultItem(varFor("y"))(pos))

Expand All @@ -62,7 +63,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
val unresolved = UnresolvedCall(ns, name, None, None)(pos)
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = None
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq(Parameter("a", CTAny)(pos))
val callResults = IndexedSeq.empty

Expand All @@ -86,7 +88,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
test("should resolve CALL my.proc.foo YIELD x, y") {
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq(Parameter("a", CTAny)(pos))
val callResults = IndexedSeq(ProcedureResultItem(varFor("x"))(pos), ProcedureResultItem(varFor("y"))(pos))
val unresolved = UnresolvedCall(ns, name, None, Some(ProcedureResult(callResults)(pos)))(pos)
Expand All @@ -111,7 +114,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
test("should resolve CALL my.proc.foo(a)") {
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq(Parameter("a", CTAny)(pos))
val callResults = IndexedSeq(ProcedureResultItem(varFor("x"))(pos), ProcedureResultItem(varFor("y"))(pos))
val unresolved = UnresolvedCall(ns, name, Some(callArguments), None)(pos)
Expand All @@ -136,7 +140,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
test("should resolve void CALL my.proc.foo(a)") {
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = None
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq(Parameter("a", CTAny)(pos))
val callResults = IndexedSeq.empty
val unresolved = UnresolvedCall(ns, name, Some(callArguments), None)(pos)
Expand All @@ -161,7 +166,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
test("should resolve CALL my.proc.foo(a) YIELD x, y AS z") {
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq(Parameter("a", CTAny)(pos))
val callResults = IndexedSeq(
ProcedureResultItem(varFor("x"))(pos),
Expand All @@ -185,7 +191,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
}

test("pretends to be based on user-declared arguments and results upon request") {
val signature = ProcedureSignature(qualifiedName, ID, IndexedSeq.empty, Some(IndexedSeq.empty), None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, IndexedSeq.empty, Some(IndexedSeq.empty), None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val call = ResolvedCall(signature, null, null, declaredArguments = false, declaredResults = false)(pos)

call.withFakedFullDeclarations.declaredArguments should be(true)
Expand All @@ -195,7 +202,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
test("adds coercion of arguments to signature types upon request") {
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq(Parameter("a", CTAny)(pos))
val callResults = IndexedSeq(
ProcedureResultItem(varFor("x"))(pos),
Expand All @@ -222,7 +230,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
test("should verify number of arguments during semantic checking of resolved calls") {
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq.empty
val callResults = IndexedSeq(
ProcedureResultItem(varFor("x"))(pos),
Expand All @@ -243,7 +252,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
test("should verify that result variables are unique during semantic checking of resolved calls") {
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq(Parameter("a", CTAny)(pos))
val callResults = IndexedSeq(
ProcedureResultItem(varFor("x"))(pos),
Expand All @@ -260,7 +270,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
test("should verify that output field names are correct during semantic checking of resolved calls") {
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq(Parameter("a", CTAny)(pos))
val callResults = IndexedSeq(
ProcedureResultItem(varFor("x"))(pos),
Expand All @@ -277,7 +288,8 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport {
test("should verify result types during semantic checking of resolved calls") {
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, ID, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None,
ProcedureReadOnlyAccess(Array.empty), id = Some(ID))
val callArguments = Seq(StringLiteral("nope")(pos))
val callResults = IndexedSeq(
ProcedureResultItem(varFor("x"))(pos),
Expand Down
Expand Up @@ -860,7 +860,6 @@ class StatementConvertersTest extends CypherFunSuite with LogicalPlanningTestSup
test("CALL foo() YIELD all RETURN all") {
val signature = ProcedureSignature(
QualifiedName(Seq.empty, "foo"),
42,
inputSignature = IndexedSeq.empty,
deprecationInfo = None,
outputSignature = Some(IndexedSeq(FieldSignature("all", CTInteger))),
Expand Down
Expand Up @@ -264,7 +264,6 @@ trait LogicalPlanningTestSupport extends CypherTestSupport with AstConstructionT
fcnLookup: Option[QualifiedName => Option[UserFunctionSignature]] = None) = {
val signature = ProcedureSignature(
QualifiedName(Seq.empty, "foo"),
42,
inputSignature = IndexedSeq.empty,
deprecationInfo = None,
outputSignature = Some(IndexedSeq(FieldSignature("all", CTInteger))),
Expand Down
Expand Up @@ -52,7 +52,7 @@ class PlanEventHorizonTest extends CypherFunSuite with LogicalPlanningTestSuppor
val qualifiedName = QualifiedName(ns.parts, name.name)
val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger))
val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode))))
val signature = ProcedureSignature(qualifiedName, 42, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty))
val callResults = IndexedSeq(ProcedureResultItem(varFor("x"))(pos), ProcedureResultItem(varFor("y"))(pos))

val call = ResolvedCall(signature, Seq.empty, callResults)(pos)
Expand Down
Expand Up @@ -22,22 +22,23 @@ package org.neo4j.cypher.internal.compatibility.v3_3
import java.lang.reflect.Constructor

import org.neo4j.cypher.internal.compatibility.v3_3.SemanticTableConverter.ExpressionMapping3To4
import org.neo4j.cypher.internal.planner.v3_4.spi.PlanningAttributes.{Cardinalities, Solveds}
import org.neo4j.cypher.internal.compiler.{v3_3 => compilerV3_3}
import org.neo4j.cypher.internal.frontend.v3_3.ast.{Expression => ExpressionV3_3}
import org.neo4j.cypher.internal.frontend.v3_3.{InputPosition => InputPositionV3_3, SemanticDirection => SemanticDirectionV3_3, ast => astV3_3, symbols => symbolsV3_3}
import org.neo4j.cypher.internal.frontend.{v3_3 => frontendV3_3}
import org.neo4j.cypher.internal.ir.{v3_3 => irV3_3, v3_4 => irV3_4}
import org.neo4j.cypher.internal.planner.v3_4.spi.PlanningAttributes.{Cardinalities, Solveds}
import org.neo4j.cypher.internal.util.v3_4.Rewritable.RewritableAny
import org.neo4j.cypher.internal.util.v3_4.attribution.{Id, IdGen, SameId, SequentialIdGen}
import org.neo4j.cypher.internal.util.v3_4.symbols.CypherType
import org.neo4j.cypher.internal.util.v3_4.{symbols => symbolsV3_4, _}
import org.neo4j.cypher.internal.util.{v3_4 => utilV3_4}
import org.neo4j.cypher.internal.v3_3.logical.plans.{LogicalPlan => LogicalPlanV3_3}
import org.neo4j.cypher.internal.v3_3.logical.{plans => plansV3_3}
import org.neo4j.cypher.internal.v3_4.expressions.{Expression => ExpressionV3_4}
import org.neo4j.cypher.internal.v3_4.logical.plans.{LogicalPlan => LogicalPlanV3_4}
import org.neo4j.cypher.internal.v3_4.logical.plans.{FieldSignature, ProcedureAccessMode, QualifiedName, LogicalPlan => LogicalPlanV3_4}
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.{Id, IdGen, SameId, SequentialIdGen}

import scala.collection.mutable
import scala.collection.mutable.{HashMap => MutableHashMap}
Expand Down Expand Up @@ -142,12 +143,31 @@ object LogicalPlanConverter {
convertVersion("frontend.v3_3", "util.v3_4", nameId, children)
case (frontendV3_3.helpers.Fby(head, tail), children: Seq[AnyRef]) => utilV3_4.Fby(children(0), children(1).asInstanceOf[utilV3_4.NonEmptyList[_]])
case (frontendV3_3.helpers.Last(head), children: Seq[AnyRef]) => utilV3_4.Last(children(0))

case ( _:plansV3_3.ProcedureSignature, children: Seq[AnyRef]) =>
plansV3_4.ProcedureSignature(children(0).asInstanceOf[QualifiedName],
children(1).asInstanceOf[IndexedSeq[FieldSignature]],
children(2).asInstanceOf[Option[IndexedSeq[FieldSignature]]],
children(3).asInstanceOf[Option[String]],
children(4).asInstanceOf[ProcedureAccessMode],
children(5).asInstanceOf[Option[String]],
children(5).asInstanceOf[Option[String]],
None)

case ( _:plansV3_3.UserFunctionSignature, children: Seq[AnyRef]) =>
plansV3_4.UserFunctionSignature(children(0).asInstanceOf[QualifiedName],
children(1).asInstanceOf[IndexedSeq[FieldSignature]],
children(2).asInstanceOf[CypherType],
children(3).asInstanceOf[Option[String]],
children(4).asInstanceOf[Array[String]],
children(5).asInstanceOf[Option[String]],
children(5).asInstanceOf[Boolean],
None)

case (item@(_: plansV3_3.CypherValue |
_: plansV3_3.QualifiedName |
_: plansV3_3.FieldSignature |
_: plansV3_3.ProcedureAccessMode |
_: plansV3_3.ProcedureSignature |
_: plansV3_3.UserFunctionSignature |
_: plansV3_3.QueryExpression[_] |
_: plansV3_3.SeekableArgs |
_: irV3_3.PatternRelationship |
Expand Down

0 comments on commit c50ae5e

Please sign in to comment.