Skip to content

Commit

Permalink
Add dumpToString to compiled plans.
Browse files Browse the repository at this point in the history
- dumpToString to compiled plans
- support for map litterals
  • Loading branch information
pontusmelke committed Apr 23, 2015
1 parent 35c6eb1 commit 0eff293
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 63 deletions.
Expand Up @@ -51,17 +51,17 @@ public static Object add( Object lhs, Object rhs )
}

// array addition
Class<?> op1Class = lhs.getClass();
Class<?> op2Class = rhs.getClass();
if ( op1Class.isArray() && op2Class.isArray())
Class<?> lhsClass = lhs.getClass();
Class<?> rhsClass = rhs.getClass();
if ( lhsClass.isArray() && rhsClass.isArray())
{
return addArrays( lhs, rhs );
return addArrays(lhs, rhs);
}
else if ( op1Class.isArray() )
else if ( lhsClass.isArray() )
{
return addArrayWithObject( lhs, rhs );
}
else if ( op2Class.isArray() )
else if ( rhsClass.isArray() )
{
return addObjectWithArray( lhs, rhs );
}
Expand Down
Expand Up @@ -50,7 +50,8 @@ object CodeGenerator {
val LONG = "long"
val INT = "int"
val OBJECT = "Object"
val OBJECTARRAY = "Object[]"
val OBJECT_ARRAY = "Object[]"
val MAP = "java.util.Map"
val DOUBLE = "double"
val STRING = "String"
val NUMBER = "Number"
Expand Down
Expand Up @@ -223,6 +223,12 @@ object LogicalPlanConverter {
val rightOp = createProjectionInstruction(rhs, context)
ProjectSubtraction(leftOp, rightOp)

case MapExpression(items: Seq[(PropertyKeyName, Expression)]) =>
val map = items.map {
case (key, expr) => (key.name, createProjectionInstruction(expr, context))
}.toMap
ProjectMap(map)

case other => throw new CantCompileQueryException(s"Projection of $other not yet supported")
}
}
Expand Down
Expand Up @@ -19,7 +19,7 @@
*/
package org.neo4j.cypher.internal.compiler.v2_3.birk.il

import org.neo4j.cypher.internal.compiler.v2_3.birk.CodeGenerator.JavaTypes.{DOUBLE, LONG, NUMBER, OBJECT, OBJECTARRAY, STRING}
import org.neo4j.cypher.internal.compiler.v2_3.birk.CodeGenerator.JavaTypes.{DOUBLE, LONG, NUMBER, OBJECT, OBJECT_ARRAY, STRING, MAP}
import org.neo4j.cypher.internal.compiler.v2_3.birk.codegen.Namer
import org.neo4j.cypher.internal.compiler.v2_3.birk.{CodeGenerator, JavaSymbol}

Expand Down Expand Up @@ -159,7 +159,22 @@ case class ProjectCollection(instructions: Seq[ProjectionInstruction]) extends P

def generateInit() = ""

def projectedVariable = JavaSymbol(instructions.map(_.projectedVariable.name).mkString("new Object[]{", ",", "}"), OBJECTARRAY)
def projectedVariable = JavaSymbol(instructions.map(_.projectedVariable.name).mkString("new Object[]{", ",", "}"), OBJECT_ARRAY)

def fields() = ""
}

case class ProjectMap(instructions: Map[String, ProjectionInstruction]) extends ProjectionInstruction {

override def children: Seq[Instruction] = instructions.values.toSeq

def generateInit() = ""

def projectedVariable = JavaSymbol(
instructions.map {
case (key, value) => s""""$key", ${value.projectedVariable.name}"""
}
.mkString("org.neo4j.helpers.collection.MapUtil.map(", ",", ")"), MAP)

def fields() = ""
}
Expand Down
Expand Up @@ -58,12 +58,6 @@ abstract class CompiledExecutionResult(completion: CompletionListener, statement
def next() = Eagerly.immutableMapValues(self.next(), makeValueJavaCompatible).asJava
}

override def dumpToString(writer: PrintWriter) = {
ensureIterator()
//todo make pretty
writer.println(innerIterator.toList)
}

override def dumpToString(): String = {
val stringWriter = new StringWriter()
val writer = new PrintWriter(stringWriter)
Expand All @@ -72,6 +66,8 @@ abstract class CompiledExecutionResult(completion: CompletionListener, statement
stringWriter.getBuffer.toString
}

override def dumpToString(writer: PrintWriter) = formatOutput(statement.readOperations, writer, columns, toList, queryStatistics())

override def queryStatistics(): InternalQueryStatistics = InternalQueryStatistics()

private var successful = false
Expand Down Expand Up @@ -141,3 +137,4 @@ abstract class CompiledExecutionResult(completion: CompletionListener, statement
def close() { self.close() }
}
}

@@ -0,0 +1,143 @@
/*
* 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.executionplan

import java.io.PrintWriter

import org.neo4j.cypher.internal.compiler.v2_3.InternalQueryStatistics
import org.neo4j.cypher.internal.compiler.v2_3.commands.values.KeyToken
import org.neo4j.cypher.internal.compiler.v2_3.helpers.IsCollection
import org.neo4j.graphdb.{Node, PropertyContainer, Relationship}
import org.neo4j.kernel.api.ReadOperations

import scala.collection.Map

/**
* Creates formatted tabular output.
*/
object formatOutput extends ((ReadOperations, PrintWriter, List[String], Seq[Map[String, Any]], InternalQueryStatistics) => Unit) {
import scala.collection.JavaConverters._

def apply(readOperations: ReadOperations, writer: PrintWriter, columns: List[String],
result: Seq[Map[String, Any]], queryStatistics: InternalQueryStatistics) {

def props(x: PropertyContainer): String = {
val (properties, propFcn, id) = x match {
case n: Node => (readOperations.nodeGetAllProperties(n.getId).asScala.map(_.propertyKeyId()), readOperations.nodeGetProperty _, n.getId )
case r: Relationship => (readOperations.relationshipGetAllProperties(r.getId).asScala.map(_.propertyKeyId()), readOperations.relationshipGetProperty _, r.getId)
}

val keyValStrings = properties.
map(pkId => readOperations.propertyKeyGetName(pkId) + ":" + text(propFcn(id, pkId).value(null)))

keyValStrings.mkString("{", ",", "}")
}

def text(a: Any): String = a match {
case x: Node => x.toString + props(x)
case x: Relationship => ":" + x.getType.name() + "[" + x.getId + "]" + props(x)
case x if x.isInstanceOf[Map[_, _]] => makeString(x.asInstanceOf[Map[String, Any]])
case x if x.isInstanceOf[java.util.Map[_, _]] => makeString(x.asInstanceOf[java.util.Map[String, Any]].asScala)
case IsCollection(coll) => coll.map(elem => text(elem)).mkString("[", ",", "]")
case x: String => "\"" + x + "\""
case v: KeyToken => v.name
case Some(x) => x.toString
case null => "<null>"
case x => x.toString
}

def textWithType(x: Any) = s"${text(x)} (${x.getClass.getSimpleName})"

def makeString(m: Map[String, Any]) = m.map {
case (k, v) => k + " -> " + text(v)
}.mkString("{", ", ", "}")

def makeSize(txt: String, wantedSize: Int): String = {
val actualSize = txt.length()
if (actualSize > wantedSize) {
txt.slice(0, wantedSize)
} else if (actualSize < wantedSize) {
txt + repeat(" ", wantedSize - actualSize)
} else txt
}

def repeat(x: String, size: Int): String = (1 to size).map((i) => x).mkString

def createString(columnSizes: Map[String, Int], m: Map[String, Any]): String = {
columns.map(c => {
val length = columnSizes.get(c).get
val txt = text(m.get(c).get)
val value = makeSize(txt, length)
value
}).mkString("| ", " | ", " |")
}

def calculateColumnSizes(result: Seq[Map[String, Any]]): Map[String, Int] = {
val columnSizes = new scala.collection.mutable.OpenHashMap[String, Int] ++ columns.map(name => name -> name.length)

result.foreach((m) => {
m.foreach((kv) => {
val length = text(kv._2).length
if (!columnSizes.contains(kv._1) || columnSizes.get(kv._1).get < length) {
columnSizes.put(kv._1, length)
}
})
})
columnSizes.toMap
}

if (columns.nonEmpty) {
val headers = columns.map((c) => Map[String, Any](c -> Some(c))).reduceLeft(_ ++ _)
val columnSizes = calculateColumnSizes(result)
val headerLine = createString(columnSizes, headers)
val lineWidth = headerLine.length - 2
val --- = "+" + repeat("-", lineWidth) + "+"

val row = if (result.size > 1) "rows" else "row"
val footer = "%d %s".format(result.size, row)

writer.println(---)
writer.println(headerLine)
writer.println(---)

result.foreach(resultLine => writer.println(createString(columnSizes, resultLine)))

writer.println(---)
writer.println(footer)

if (queryStatistics.containsUpdates) {
writer.print(queryStatistics.toString)
}
} else {
if (queryStatistics.containsUpdates) {
writer.println("+-------------------+")
writer.println("| No data returned. |")
writer.println("+-------------------+")
writer.print(queryStatistics.toString)
} else {
writer.println("+--------------------------------------------+")
writer.println("| No data returned, and nothing was changed. |")
writer.println("+--------------------------------------------+")
}
}
}


}

0 comments on commit 0eff293

Please sign in to comment.