Skip to content

Commit

Permalink
Added support for addition in compiled plans
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusmelke committed Apr 14, 2015
1 parent 8397a17 commit 584bcb4
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 57 deletions.
@@ -0,0 +1,153 @@
/**
* Copyright (c) 2002-2015 "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.cypher.internal.compiler.v2_3.birk;

import org.neo4j.cypher.internal.compiler.v2_3.CypherTypeException;

import java.lang.reflect.Array;
import java.util.Arrays;

/**
* This is a helper class used by compiled plans for doing basic math operations
*/
public final class CompiledMathHelper
{
/**
* Do not instantiate this class
*/
private CompiledMathHelper()
{
}

/**
* Utility function for doing addition
*/
public static Object add( Object op1, Object op2 )
{
if ( op1 == null || op2 == null )
{
return null;
}

if ( op1 instanceof String || op2 instanceof String )
{
return String.valueOf( op1 ) + String.valueOf( op2 );
}

//array multiplication
Class<?> op1Class = op1.getClass();
Class<?> op2Class = op2.getClass();
if ( op1Class.isArray() && op2Class.isArray())
{
return addArrays( op1, op2 );
}
else if ( op1Class.isArray() )
{
return addArrayWithObject( op1, op2 );
}
else if ( op2Class.isArray() )
{
return addObjectWithArray( op1, op2 );
}


//From here down we assume we are dealing with numbers
if( !(op1 instanceof Number) || !(op2 instanceof Number) ){
throw new CypherTypeException( "Cannot add " + op1.getClass().getSimpleName() + " and " + op2.getClass()
.getSimpleName(), null );
}

if ( op1 instanceof Long || op2 instanceof Long )
{
return ((Number) op1).longValue() + ((Number) op2).longValue();
}

if ( op1 instanceof Double || op2 instanceof Double )
{
return ((Number) op1).doubleValue() + ((Number) op2).doubleValue();
}

if ( op1 instanceof Float || op2 instanceof Float )
{
return ((Number) op1).floatValue() + ((Number) op2).floatValue();
}

if ( op1 instanceof Integer || op2 instanceof Integer )
{
return ((Number) op1).intValue() + ((Number) op2).intValue();
}


throw new CypherTypeException( "Cannot add " + op1.getClass().getSimpleName() + " and " + op2.getClass()
.getSimpleName(), null );
}

/**
* Both a1 and a2 must be arrays
*/
private static Object addArrays( Object a1, Object a2 )
{
int l1 = Array.getLength( a1 );
int l2 = Array.getLength( a2 );
Object[] ret = new Object[l1 + l2];
for ( int i = 0; i < l1; i++ )
{
ret[i] = Array.get( a1, i );
}
for ( int i = 0; i < l2; i++ )
{
ret[l1 + i] = Array.get( a2, i );
}
return ret;
}

/**
* array must be an array
*/
private static Object addArrayWithObject( Object array, Object object )
{
int l = Array.getLength( array );
Object[] ret = new Object[l + 1];
int i = 0;
for (; i < l; i++ )
{
ret[i] = Array.get( array, i );
}
ret[i] = object;

return ret;
}

/**
* array must be an array
*/
private static Object addObjectWithArray( Object object, Object array )
{
int l = Array.getLength( array );
Object[] ret = new Object[l + 1];
ret[0] = object;
for (int i = 1; i < ret.length; i++ )
{
ret[i] = Array.get( array, i );
}

return ret;
}
}
Expand Up @@ -92,11 +92,11 @@ public static Class<InternalExecutionResult> compile( String className, String c
} }


public static InternalExecutionResult newInstance( Class<InternalExecutionResult> clazz, Statement statement, public static InternalExecutionResult newInstance( Class<InternalExecutionResult> clazz, Statement statement,
GraphDatabaseService db, ExecutionMode executionMode, InternalPlanDescription description, HashMap<String, Object> params) GraphDatabaseService db, ExecutionMode executionMode, InternalPlanDescription description, Map<String, Object> params)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException
{ {
Constructor<InternalExecutionResult> constructor = Constructor<InternalExecutionResult> constructor =
clazz.getDeclaredConstructor( Statement.class, GraphDatabaseService.class, ExecutionMode.class, InternalPlanDescription.class , HashMap.class); clazz.getDeclaredConstructor( Statement.class, GraphDatabaseService.class, ExecutionMode.class, InternalPlanDescription.class , Map.class);
return constructor.newInstance( statement, db, executionMode, description, params ); return constructor.newInstance( statement, db, executionMode, description, params );
} }


Expand Down
Expand Up @@ -25,8 +25,8 @@ import java.util.concurrent.atomic.AtomicInteger
import org.neo4j.cypher.internal.ExecutionMode import org.neo4j.cypher.internal.ExecutionMode
import org.neo4j.cypher.internal.compiler.v2_3.helpers.Eagerly import org.neo4j.cypher.internal.compiler.v2_3.helpers.Eagerly
import org.neo4j.cypher.internal.compiler.v2_3.{PropertyKeyId, CostPlannerName} import org.neo4j.cypher.internal.compiler.v2_3.{PropertyKeyId, CostPlannerName}
import org.neo4j.cypher.internal.compiler.v2_3.ast.{Literal, Parameter, Identifier, Property} import org.neo4j.cypher.internal.compiler.v2_3.ast._
import org.neo4j.cypher.internal.compiler.v2_3.birk.CodeGenerator.JavaTypes.{INT, LONG, OBJECT} import org.neo4j.cypher.internal.compiler.v2_3.birk.CodeGenerator.JavaTypes.{INT, LONG, OBJECT, DOUBLE, STRING}
import org.neo4j.cypher.internal.compiler.v2_3.birk.il._ import org.neo4j.cypher.internal.compiler.v2_3.birk.il._
import org.neo4j.cypher.internal.compiler.v2_3.executionplan.{PlanFingerprint, CompiledPlan} import org.neo4j.cypher.internal.compiler.v2_3.executionplan.{PlanFingerprint, CompiledPlan}
import org.neo4j.cypher.internal.compiler.v2_3.planDescription.InternalPlanDescription import org.neo4j.cypher.internal.compiler.v2_3.planDescription.InternalPlanDescription
Expand Down Expand Up @@ -80,6 +80,10 @@ object CodeGenerator {
val LONG = "long" val LONG = "long"
val INT = "int" val INT = "int"
val OBJECT = "Object" val OBJECT = "Object"
val OBJECTARRAY = "Object[]"
val DOUBLE = "double"
val STRING = "String"
val NUMBER = "Number"
} }
} }


Expand Down Expand Up @@ -174,7 +178,7 @@ class CodeGenerator(semanticTable: SemanticTable) {
|import org.neo4j.graphdb.Result; |import org.neo4j.graphdb.Result;
|import org.neo4j.cypher.internal.compiler.v2_3.planDescription.InternalPlanDescription; |import org.neo4j.cypher.internal.compiler.v2_3.planDescription.InternalPlanDescription;
|import org.neo4j.cypher.internal.ExecutionMode; |import org.neo4j.cypher.internal.ExecutionMode;
|import java.util.HashMap; |import java.util.Map;
| |
|$imports |$imports
| |
Expand All @@ -184,9 +188,9 @@ class CodeGenerator(semanticTable: SemanticTable) {
|private final GraphDatabaseService db; |private final GraphDatabaseService db;
|private final InternalPlanDescription description; |private final InternalPlanDescription description;
|private final ExecutionMode executionMode; |private final ExecutionMode executionMode;
|private final HashMap<String, Object> params; |private final Map<String, Object> params;
| |
|public $className( Statement statement, GraphDatabaseService db, ExecutionMode executionMode, InternalPlanDescription description, HashMap<String, Object> params ) |public $className( Statement statement, GraphDatabaseService db, ExecutionMode executionMode, InternalPlanDescription description, Map<String, Object> params )
|{ |{
| this.ro = statement.readOperations( ); | this.ro = statement.readOperations( );
| this.db = db; | this.db = db;
Expand Down Expand Up @@ -300,43 +304,54 @@ class CodeGenerator(semanticTable: SemanticTable) {
val (x, action) = consume(stack.top, plan, stack.pop) val (x, action) = consume(stack.top, plan, stack.pop)
(x, WhileLoop(relVar, ExpandC(variables(fromNode).name, relVar.name, dir, relTypes.map(t => variableName.next -> t.name).toMap,nodeVar.name, action), Instruction.empty)) (x, WhileLoop(relVar, ExpandC(variables(fromNode).name, relVar.name, dir, relTypes.map(t => variableName.next -> t.name).toMap,nodeVar.name, action), Instruction.empty))


case Projection(left, expressions) => case Projection(_, expressions) =>
val projectionInstructions = def findProjectionInstruction(expression: Expression): ProjectionInstruction = expression match {
expressions.map { case nodeOrRel@Identifier(name) if semanticTable.isNode(nodeOrRel) || semanticTable.isRelationship(nodeOrRel) =>
case (identifier, nodeOrRel@Identifier(name)) if semanticTable.isNode(nodeOrRel) || semanticTable.isRelationship(nodeOrRel) => ProjectNodeOrRelationship(variables(name))
//just project the new names
variables += identifier -> variables(name) case Property(node@Identifier(name), propKey) if semanticTable.isNode(node) =>
None val token = propKey.id(semanticTable).map(_.id)

ProjectNodeProperty(token, propKey.name, variables(name).name, variableName)
case (identifier, Property(node@Identifier(name), propKey)) if semanticTable.isNode(node) =>
val token = propKey.id(semanticTable).map(_.id) case Property(rel@Identifier(name), propKey) if semanticTable.isRelationship(rel) =>
val projection = ProjectNodeProperty(token, propKey.name, variables(name).name, variableName) val token = propKey.id(semanticTable).map(_.id)
variables += identifier -> projection.projectedVariable ProjectRelProperty(token, propKey.name, variables(name).name, variableName)
Some(projection)

case Parameter(name) => ProjectParameter(name)
case (identifier, Property(rel@Identifier(name), propKey)) if semanticTable.isRelationship(rel) =>
val token = propKey.id(semanticTable).map(_.id) case lit: IntegerLiteral =>
val projection = ProjectRelProperty(token, propKey.name, variables(name).name, variableName) ProjectLiteral(JavaSymbol(s"${lit.value.toString}L", LONG))
variables += identifier -> projection.projectedVariable
Some(projection) case lit: DoubleLiteral =>

ProjectLiteral(JavaSymbol(lit.value.toString, DOUBLE))
case (identifier, Parameter(name)) =>
val projection = ProjectParameter(name) case lit: StringLiteral =>
variables += identifier -> projection.projectedVariable ProjectLiteral(JavaSymbol(s""""${lit.value}"""", STRING))
Some(projection)
//TODO be smarter about boxing case lit: Literal =>
case (identifier, lit: Literal) => ProjectLiteral(JavaSymbol(lit.value.toString, OBJECT))
val projection = ProjectLiteral(lit.value)
variables += identifier -> projection.projectedVariable case Collection(exprs) =>
Some(projection) ProjectCollection(exprs.map(findProjectionInstruction))


case other => throw new CantCompileQueryException(s"Projections of $other not yet supported") case Add(lhs, rhs) =>

val leftOp = findProjectionInstruction(lhs)
}.flatten.toSeq val rightOp = findProjectionInstruction(rhs)
ProjectAddition(leftOp, rightOp)

case other => throw new CantCompileQueryException(s"Projections of $other not yet supported")
}

val projectionInstructions = expressions.map {
case (identifier, expression) =>
val instruction = findProjectionInstruction(expression)
variables += identifier -> instruction.projectedVariable
instruction
}.toSeq


val (x, action) = consume(stack.top, plan, stack.pop) val (x, action) = consume(stack.top, plan, stack.pop)


(x, ProjectNodeProperties(projectionInstructions, action)) (x, ProjectProperties(projectionInstructions, action))


case _ => throw new CantCompileQueryException(s"$plan is not yet supported") case _ => throw new CantCompileQueryException(s"$plan is not yet supported")
} }
Expand Down

0 comments on commit 584bcb4

Please sign in to comment.