From 48af9051b4fea090aa83eef04436ec10694a3705 Mon Sep 17 00:00:00 2001 From: Pontus Melke Date: Sun, 26 Jun 2016 09:40:53 +0200 Subject: [PATCH] Introduce Neo4jValue abstraction --- .../procs/ProcedureCallExecutionPlan.scala | 2 +- .../execution/PipeExecutionPlanBuilder.scala | 2 +- .../v3_1/spi/ProcedureSignature.scala | 3 +- .../v3_1/TransactionBoundPlanContext.scala | 5 +- .../kernel/api/proc/ProcedureSignature.java | 7 +- .../impl/proc/MethodSignatureCompiler.java | 2 +- .../neo4j/kernel/impl/proc/Neo4jValue.java | 64 +++++++++++++++++++ .../neo4j/kernel/impl/proc/TypeMappers.java | 36 ++++++++--- 8 files changed, 103 insertions(+), 18 deletions(-) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/proc/Neo4jValue.java diff --git a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/procs/ProcedureCallExecutionPlan.scala b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/procs/ProcedureCallExecutionPlan.scala index 8fb34a0d3833a..a028dbd89b253 100644 --- a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/procs/ProcedureCallExecutionPlan.scala +++ b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/procs/ProcedureCallExecutionPlan.scala @@ -50,7 +50,7 @@ case class ProcedureCallExecutionPlan(signature: ProcedureSignature, extends ExecutionPlan { private val argExprCommands: Seq[expressions.Expression] = argExprs.map(toCommandExpression) ++ - signature.inputSignature.drop(argExprs.size).flatMap(_.default).map(Literal(_)) + signature.inputSignature.drop(argExprs.size).flatMap(_.default).map(o => Literal(o.value)) override def run(ctx: QueryContext, planType: ExecutionMode, params: Map[String, Any]): InternalExecutionResult = { val input = evaluateArguments(ctx, params) diff --git a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/planner/execution/PipeExecutionPlanBuilder.scala b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/planner/execution/PipeExecutionPlanBuilder.scala index 84ada81725074..893cde8dec8cf 100644 --- a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/planner/execution/PipeExecutionPlanBuilder.scala +++ b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/planner/execution/PipeExecutionPlanBuilder.scala @@ -314,7 +314,7 @@ case class ActualPipeBuilder(monitors: Monitors, recurse: LogicalPlan => Pipe, r case ProcedureCall(_, call@ResolvedCall(signature, callArguments, callResults, _, _)) => val callMode = ProcedureCallMode.fromAccessMode(signature.accessMode) - val callArgumentCommands = callArguments.map(Some(_)).zipAll(signature.inputSignature.map(_.default), None, None).map { + val callArgumentCommands = callArguments.map(Some(_)).zipAll(signature.inputSignature.map(_.default.map(_.value)), None, None).map { case (given, default) => given.map(toCommandExpression).getOrElse(Literal(default.get)) } val rowProcessing = ProcedureCallRowProcessing(signature) diff --git a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/ProcedureSignature.scala b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/ProcedureSignature.scala index b759249434f4d..8f4f22f5b5b6d 100644 --- a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/ProcedureSignature.scala +++ b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/ProcedureSignature.scala @@ -41,7 +41,8 @@ case class QualifiedProcedureName(namespace: Seq[String], name: String) { override def toString = s"""${namespace.mkString(".")}.$name""" } -case class FieldSignature(name: String, typ: CypherType, default: Option[AnyRef] = None) +case class CypherValue(value: AnyRef, cypherType: CypherType) +case class FieldSignature(name: String, typ: CypherType, default: Option[CypherValue] = None) sealed trait ProcedureAccessMode diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundPlanContext.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundPlanContext.scala index fea3615616cb9..01fe4f4ea497b 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundPlanContext.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundPlanContext.scala @@ -36,6 +36,7 @@ import org.neo4j.kernel.api.exceptions.schema.SchemaKernelException import org.neo4j.kernel.api.index.{IndexDescriptor, InternalIndexState} import org.neo4j.kernel.api.proc.Neo4jTypes.AnyType import org.neo4j.kernel.api.proc.{Neo4jTypes, ProcedureSignature => KernelProcedureSignature} +import org.neo4j.kernel.impl.proc.Neo4jValue import scala.collection.JavaConverters._ @@ -132,7 +133,7 @@ class TransactionBoundPlanContext(tc: TransactionalContextWrapperv3_1) override def procedureSignature(name: QualifiedProcedureName) = { val kn = new KernelProcedureSignature.ProcedureName(name.namespace.asJava, name.name) val ks = tc.statement.readOperations().procedureGet(kn) - val input = ks.inputSignature().asScala.map(s => FieldSignature(s.name(), asCypherType(s.neo4jType()), asOption(s.defaultValue()))).toIndexedSeq + val input = ks.inputSignature().asScala.map(s => FieldSignature(s.name(), asCypherType(s.neo4jType()), asOption(s.defaultValue()).map(asCypherValue))).toIndexedSeq val output = if (ks.isVoid) None else Some(ks.outputSignature().asScala.map(s => FieldSignature(s.name(), asCypherType(s.neo4jType()))).toIndexedSeq) val mode = asCypherProcMode(ks.mode()) @@ -150,6 +151,8 @@ class TransactionBoundPlanContext(tc: TransactionalContextWrapperv3_1) "Unable to execute procedure, because it requires an unrecognized execution mode: " + mode.name(), null ) } + private def asCypherValue(neo4jValue: Neo4jValue) = CypherValue(neo4jValue.value, asCypherType(neo4jValue.neo4jType())) + private def asCypherType(neoType: AnyType): CypherType = neoType match { case Neo4jTypes.NTString => symbols.CTString case Neo4jTypes.NTInteger => symbols.CTInteger diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/proc/ProcedureSignature.java b/community/kernel/src/main/java/org/neo4j/kernel/api/proc/ProcedureSignature.java index c13bcb17feed7..fd0416970d5ae 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/proc/ProcedureSignature.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/proc/ProcedureSignature.java @@ -28,6 +28,7 @@ import org.neo4j.helpers.collection.Iterables; import org.neo4j.kernel.api.proc.Neo4jTypes.AnyType; +import org.neo4j.kernel.impl.proc.Neo4jValue; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; @@ -97,14 +98,14 @@ public static class FieldSignature { private final String name; private final AnyType type; - private final Optional defaultValue; + private final Optional defaultValue; public FieldSignature( String name, AnyType type) { this(name, type, Optional.empty()); } - public FieldSignature( String name, AnyType type, Optional defaultValue ) + public FieldSignature( String name, AnyType type, Optional defaultValue ) { this.name = name; this.type = type; @@ -121,7 +122,7 @@ public AnyType neo4jType() return type; } - public Optional defaultValue() + public Optional defaultValue() { return defaultValue; } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/MethodSignatureCompiler.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/MethodSignatureCompiler.java index d9f07884fe600..6a68ced7e578a 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/MethodSignatureCompiler.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/MethodSignatureCompiler.java @@ -77,7 +77,7 @@ public List signatureFor( Method method ) throws ProcedureExcept try { NeoValueConverter valueConverter = typeMappers.converterFor( type ); - Optional defaultValue = valueConverter.defaultValue( parameter ); + Optional defaultValue = valueConverter.defaultValue( parameter ); //it is not allowed to have holes in default values if (seenDefault && !defaultValue.isPresent()) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/Neo4jValue.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/Neo4jValue.java new file mode 100644 index 0000000000000..0c780c5736b9c --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/Neo4jValue.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002-2016 "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 . + */ +package org.neo4j.kernel.impl.proc; + +import org.neo4j.kernel.api.proc.Neo4jTypes; + +public class Neo4jValue +{ + private final Object value; + private final Neo4jTypes.AnyType type; + + public Neo4jValue( Object value, Neo4jTypes.AnyType type ) + { + this.value = value; + this.type = type; + } + + public Object value() + { + return value; + } + + public Neo4jTypes.AnyType neo4jType() + { + return type; + } + + public static Neo4jValue ntString(String value) + { + return new Neo4jValue( value, Neo4jTypes.NTString ); + } + + public static Neo4jValue ntInteger(long value) + { + return new Neo4jValue( value, Neo4jTypes.NTInteger ); + } + + public static Neo4jValue ntFloat(double value) + { + return new Neo4jValue( value, Neo4jTypes.NTFloat ); + } + + public static Neo4jValue ntBoolean(boolean value) + { + return new Neo4jValue( value, Neo4jTypes.NTBoolean ); + } +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/TypeMappers.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/TypeMappers.java index 7445eb6b8fdd9..2f7bfd2e4493a 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/TypeMappers.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/TypeMappers.java @@ -33,6 +33,9 @@ import org.neo4j.kernel.api.proc.Neo4jTypes.AnyType; import org.neo4j.procedure.Name; +import static java.lang.Boolean.parseBoolean; +import static java.lang.Double.parseDouble; +import static java.lang.Long.parseLong; import static org.neo4j.kernel.api.proc.Neo4jTypes.NTAny; import static org.neo4j.kernel.api.proc.Neo4jTypes.NTBoolean; import static org.neo4j.kernel.api.proc.Neo4jTypes.NTFloat; @@ -41,6 +44,9 @@ import static org.neo4j.kernel.api.proc.Neo4jTypes.NTMap; import static org.neo4j.kernel.api.proc.Neo4jTypes.NTNumber; import static org.neo4j.kernel.api.proc.Neo4jTypes.NTString; +import static org.neo4j.kernel.impl.proc.Neo4jValue.ntBoolean; +import static org.neo4j.kernel.impl.proc.Neo4jValue.ntFloat; +import static org.neo4j.kernel.impl.proc.Neo4jValue.ntInteger; public class TypeMappers { @@ -53,7 +59,7 @@ interface NeoValueConverter { AnyType type(); Object toNeoValue( Object javaValue ) throws ProcedureException; - Optional defaultValue(Name parameter) throws ProcedureException; + Optional defaultValue(Name parameter) throws ProcedureException; } private final Map javaToNeo = new HashMap<>(); @@ -129,11 +135,20 @@ public void registerType( Class javaClass, NeoValueConverter toNeo ) } private final NeoValueConverter TO_ANY = new SimpleConverter( NTAny, Object.class ); - private final NeoValueConverter TO_STRING = new SimpleConverter( NTString, String.class, s -> s); - private final NeoValueConverter TO_INTEGER = new SimpleConverter( NTInteger, Long.class, Long::parseLong ); - private final NeoValueConverter TO_FLOAT = new SimpleConverter( NTFloat, Double.class, Double::parseDouble); - private final NeoValueConverter TO_NUMBER = new SimpleConverter( NTNumber, Number.class, Boolean::parseBoolean); - private final NeoValueConverter TO_BOOLEAN = new SimpleConverter( NTBoolean, Boolean.class, Boolean::parseBoolean); + private final NeoValueConverter TO_STRING = new SimpleConverter( NTString, String.class, Neo4jValue::ntString ); + private final NeoValueConverter TO_INTEGER = new SimpleConverter( NTInteger, Long.class, s -> ntInteger( parseLong(s) ) ); + private final NeoValueConverter TO_FLOAT = new SimpleConverter( NTFloat, Double.class, s -> ntFloat( parseDouble(s) )); + private final NeoValueConverter TO_NUMBER = new SimpleConverter( NTNumber, Number.class, s -> { + try + { + return ntInteger( parseLong(s) ); + } + catch ( NumberFormatException e ) + { + return ntFloat( parseDouble( s ) ); + } + }); + private final NeoValueConverter TO_BOOLEAN = new SimpleConverter( NTBoolean, Boolean.class, s -> ntBoolean( parseBoolean(s) )); private final NeoValueConverter TO_MAP = new SimpleConverter( NTMap, Map.class); private final NeoValueConverter TO_LIST = toList( TO_ANY ); @@ -159,23 +174,24 @@ public static class SimpleConverter implements NeoValueConverter { private final AnyType type; private final Class javaClass; - private final Function defaultConverter; + private final Function defaultConverter; public SimpleConverter( AnyType type, Class javaClass) { this( type, javaClass, s -> { - throw new UnsupportedOperationException( String.format("Default values for type %s is not supported", javaClass.getSimpleName() )); + throw new UnsupportedOperationException( + String.format("Default values for type %s is not supported", javaClass.getSimpleName() )); } ); } - public SimpleConverter( AnyType type, Class javaClass, Function defaultConverter ) + public SimpleConverter( AnyType type, Class javaClass, Function defaultConverter ) { this.type = type; this.javaClass = javaClass; this.defaultConverter = defaultConverter; } - public Optional defaultValue(Name parameter) throws ProcedureException + public Optional defaultValue(Name parameter) throws ProcedureException { String defaultValue = parameter.defaultValue(); if ( defaultValue.equals( Name.DEFAULT_VALUE ) )