diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java index 539d127e918c4..0c53c524ae3e4 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java @@ -23,6 +23,7 @@ import java.util.function.Supplier; import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.values.virtual.MapValue; public interface BoltMessageLogger { @@ -42,7 +43,7 @@ public interface BoltMessageLogger void logInit( String userAgent ); - void logRun( String statement, Supplier> parametersSupplier ); + void logRun( String statement, Supplier parametersSupplier ); void logPullAll(); @@ -52,7 +53,7 @@ public interface BoltMessageLogger void logReset(); - void logSuccess( Supplier metadataSupplier ); + void logSuccess( Supplier metadataSupplier ); void logFailure( Status status ); diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java index f9da95472ce18..67b2406a5bf2d 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java @@ -20,6 +20,10 @@ package org.neo4j.bolt.logging; +import io.netty.channel.Channel; +import io.netty.util.AttributeKey; +import org.codehaus.jackson.map.ObjectMapper; + import java.io.IOException; import java.util.Map; import java.util.UUID; @@ -27,11 +31,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; -import io.netty.channel.Channel; -import io.netty.util.AttributeKey; -import org.codehaus.jackson.map.ObjectMapper; - import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.values.AnyValue; +import org.neo4j.values.utils.PrettyPrinter; +import org.neo4j.values.virtual.MapValue; import static java.lang.String.format; @@ -107,9 +110,9 @@ public void logInit( String userAgent ) } @Override - public void logRun( String statement, Supplier> parametersSupplier ) + public void logRun( String statement, Supplier parametersSupplier ) { - clientEvent( "RUN", () -> format( "%s %s", json(statement), json( parametersSupplier.get() ) ) ); + clientEvent( "RUN", () -> format( "%s %s", json( statement ), formatAnyValue( parametersSupplier.get() ) ) ); } @Override @@ -137,9 +140,9 @@ public void logReset() } @Override - public void logSuccess( Supplier metadataSupplier ) + public void logSuccess( Supplier metadataSupplier ) { - serverEvent( "SUCCESS", () -> json( metadataSupplier.get() ) ); + serverEvent( "SUCCESS", () -> formatAnyValue( metadataSupplier.get() ) ); } @Override @@ -206,4 +209,10 @@ private static String json( Object arg ) } } + private static String formatAnyValue( AnyValue mapValue ) + { + PrettyPrinter printer = new PrettyPrinter(); + mapValue.writeTo( printer ); + return printer.value(); + } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java index 7b826bd65b9d3..a3fa7115cc7b7 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java @@ -23,6 +23,7 @@ import java.util.function.Supplier; import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.values.virtual.MapValue; public class NullBoltMessageLogger implements BoltMessageLogger { @@ -78,7 +79,7 @@ public void logInit( String userAgent ) } @Override - public void logRun( String statement, Supplier> parametersSupplier ) + public void logRun( String statement, Supplier parametersSupplier ) { } @@ -103,7 +104,7 @@ public void logReset() } @Override - public void logSuccess( Supplier metadataSupplier ) + public void logSuccess( Supplier metadataSupplier ) { } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriter.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriter.java index 04ad8a712d589..2a7693b8834bb 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriter.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriter.java @@ -23,13 +23,14 @@ import java.util.function.Supplier; import org.neo4j.bolt.logging.BoltMessageLogger; -import org.neo4j.cypher.internal.javacompat.BaseToObjectValueWriter; import org.neo4j.cypher.result.QueryResult; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.spatial.Point; +import org.neo4j.helpers.BaseToObjectValueWriter; import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.values.AnyValue; +import org.neo4j.values.utils.PrettyPrinter; import org.neo4j.values.virtual.MapValue; import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.FAILURE; @@ -84,7 +85,7 @@ public void onRecord( QueryResult.Record item ) throws IOException @Override public void onSuccess( MapValue metadata ) throws IOException { - messageLogger.logSuccess( metadataSupplier( metadata ) ); + messageLogger.logSuccess( () -> metadata ); packer.packStructHeader( 1, SUCCESS.signature() ); packer.packRawMap( metadata ); onMessageComplete.onMessageComplete(); @@ -94,9 +95,9 @@ private Supplier metadataSupplier( MapValue metadata ) { return () -> { - MapToObjectWriter writer = new MapToObjectWriter(); - metadata.writeTo( writer ); - return writer.value().toString(); + PrettyPrinter printer = new PrettyPrinter(); + metadata.writeTo( printer ); + return printer.value(); }; } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java b/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java index 229a63a8c77cc..c2cdcfe477ed6 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java @@ -19,8 +19,6 @@ */ package org.neo4j.bolt.logging; -import java.net.InetSocketAddress; - import io.netty.channel.Channel; import io.netty.util.Attribute; import org.junit.Before; @@ -29,16 +27,21 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import java.net.InetSocketAddress; + import org.neo4j.bolt.v1.runtime.Neo4jError; +import org.neo4j.helpers.ValueUtils; +import org.neo4j.helpers.collection.MapUtil; import org.neo4j.kernel.DeadlockDetectedException; +import org.neo4j.values.virtual.VirtualValues; import static java.util.Collections.singletonMap; - import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; - import static org.neo4j.bolt.logging.BoltMessageLoggerImpl.CORRELATION_ATTRIBUTE_KEY; +import static org.neo4j.helpers.ValueUtils.asMapValue; +import static org.neo4j.helpers.collection.MapUtil.map; @RunWith( MockitoJUnitRunner.class ) public class BoltMessageLoggerImplTest @@ -118,9 +121,9 @@ public void logInit() throws Exception public void logRun() throws Exception { // when - boltMessageLogger.logRun( "RETURN 42", () -> singletonMap( "param1", "value" ) ); + boltMessageLogger.logRun( "RETURN 42", () -> asMapValue( singletonMap( "param1", "value" ) ) ); // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C RUN \"RETURN 42\" {\"param1\":\"value\"}" ); + verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C RUN \"RETURN 42\" {param1: \"value\"}" ); } @Test @@ -154,9 +157,9 @@ public void logReset() throws Exception public void logSuccess() throws Exception { // when - boltMessageLogger.logSuccess( () -> "metadata" ); + boltMessageLogger.logSuccess( () -> asMapValue( map( "key", "value" ) ) ); // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "S SUCCESS \"metadata\"" ); + verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "S SUCCESS {key: \"value\"}" ); } @Test diff --git a/community/values/src/main/java/org/neo4j/values/storable/ValueWriter.java b/community/values/src/main/java/org/neo4j/values/storable/ValueWriter.java index 3d0e31d4c119c..8134a94992db0 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/ValueWriter.java +++ b/community/values/src/main/java/org/neo4j/values/storable/ValueWriter.java @@ -66,7 +66,7 @@ enum ArrayType default void writeUTF8( byte[] bytes, int offset, int length ) throws E { - writeString( new String(bytes, offset, length, StandardCharsets.UTF_8) ); + writeString( new String( bytes, offset, length, StandardCharsets.UTF_8 ) ); } default void writeString( char[] value ) throws E diff --git a/community/values/src/main/java/org/neo4j/values/utils/PrettyPrinter.java b/community/values/src/main/java/org/neo4j/values/utils/PrettyPrinter.java index 9c5ebc98d2351..338b413617e88 100644 --- a/community/values/src/main/java/org/neo4j/values/utils/PrettyPrinter.java +++ b/community/values/src/main/java/org/neo4j/values/utils/PrettyPrinter.java @@ -1,3 +1,22 @@ +/* + * Copyright (c) 2002-2017 "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.values.utils; import java.util.ArrayDeque; @@ -15,154 +34,33 @@ /** * Pretty printer for AnyValues. - * - * Used to nicely format any given AnyValue. + *

+ * Used to format AnyValue as a json-like string, as following: + *

    + *
  • nodes: (id=42 :LABEL {prop1: ["a", 13]})
  • + *
  • edges: -[id=42 :TYPE {prop1: ["a", 13]}]-
  • + *
  • paths: (id=1 :L)-[id=42 :T {k: "v"}]->(id=2)-...
  • + *
  • points are serialized to geojson
  • + *
  • maps: {foo: 42, bar: "baz"}
  • + *
  • lists and arrays: ["aa", "bb", "cc"]
  • + *
  • Numbers: 2.7182818285
  • + *
  • Strings: "this is a string"
  • + *
*/ public class PrettyPrinter implements AnyValueWriter { private final Deque stack = new ArrayDeque<>(); + private final String quoteMark; - PrettyPrinter() - { - stack.push( new ValueWriter() ); - } - - interface Writer - { - void write( String value ); - - String done(); - - default String quote( String in ) - { - return "'" + in + "'"; - } - } - - static class ValueWriter implements Writer - { - private final StringBuilder builder = new StringBuilder(); - - @Override - public void write( String value ) - { - builder.append( value ); - } - - @Override - public String done() - { - return builder.toString(); - } - } - - static class MapWriter implements Writer - { - private boolean writeKey = true; - private String sep = ""; - private final StringBuilder builder; - - MapWriter() - { - this.builder = new StringBuilder( "{" ); - } - - @Override - public void write( String value ) - { - if ( writeKey ) - { - builder.append( sep ).append( value ).append( ": " ); - } - else - { - builder.append( value ); - } - writeKey = !writeKey; - sep = ", "; - } - - @Override - public String done() - { - return builder.append( "}" ).toString(); - } - - @Override - public String quote( String in ) - { - return writeKey ? in : "'" + in + "'"; - } - } - - static class ListWriter implements Writer - { - private final StringBuilder builder; - private String sep = ""; - - ListWriter() - { - this.builder = new StringBuilder( "[" ); - } - - @Override - public void write( String value ) - { - builder.append( sep ).append( value ); - sep = ", "; - } - - @Override - public String done() - { - return builder.append( "]" ).toString(); - } - } - - static class PointWriter implements Writer - { - private int i = 0; - private String[] coordinates = new String[2]; - private final CoordinateReferenceSystem crs; - - PointWriter( CoordinateReferenceSystem crs ) - { - this.crs = crs; - } - - @Override - public void write( String value ) - { - coordinates[i++] = value; - } - - @Override - public String done() - { - return format( - "{geometry: " + - "{type: 'Point', coordinates: [%s, %s], crs: " + - "{type: link, " + - "properties: {href: '%s', code: %d}" + - "}" + - "}" + - "}", - coordinates[0], coordinates[1], crs.href(), crs.code() ); - } - } - - private void append( String value ) + public PrettyPrinter() { - assert !stack.isEmpty(); - Writer head = stack.peek(); - head.write( value ); + this( "\"" ); } - private String quote( String value ) + public PrettyPrinter( String quoteMark ) { - assert !stack.isEmpty(); - Writer head = stack.peek(); - return head.quote( value ); + this.quoteMark = quoteMark; + stack.push( new ValueWriter() ); } @Override @@ -364,4 +262,148 @@ public String value() assert stack.size() == 1; return stack.getLast().done(); } + + private void append( String value ) + { + assert !stack.isEmpty(); + Writer head = stack.peek(); + head.append( value ); + } + + private String quote( String value ) + { + assert !stack.isEmpty(); + Writer head = stack.peek(); + return head.quote( value ); + } + + private interface Writer + { + void append( String value ); + + String done(); + + String quote( String in ); + } + + private abstract class BaseWriter implements Writer + { + protected final StringBuilder builder = new StringBuilder(); + + @Override + public String done() + { + return builder.toString(); + } + + @Override + public String quote( String in ) + { + return quoteMark + in + quoteMark; + } + } + + private class ValueWriter extends BaseWriter + { + @Override + public void append( String value ) + { + builder.append( value ); + } + } + + private class MapWriter extends BaseWriter + { + private boolean writeKey = true; + private String sep = ""; + + MapWriter() + { + super(); + builder.append( "{" ); + } + + @Override + public void append( String value ) + { + if ( writeKey ) + { + builder.append( sep ).append( value ).append( ": " ); + } + else + { + builder.append( value ); + } + writeKey = !writeKey; + sep = ", "; + } + + @Override + public String done() + { + return builder.append( "}" ).toString(); + } + + @Override + public String quote( String in ) + { + return writeKey ? in : super.quote( in ); + } + } + + private class ListWriter extends BaseWriter + { + private String sep = ""; + + ListWriter() + { + super(); + builder.append( "[" ); + } + + @Override + public void append( String value ) + { + builder.append( sep ).append( value ); + sep = ", "; + } + + @Override + public String done() + { + return builder.append( "]" ).toString(); + } + } + + private class PointWriter extends BaseWriter + { + private int index; + private String[] coordinates = new String[2]; + private final CoordinateReferenceSystem crs; + + PointWriter( CoordinateReferenceSystem crs ) + { + this.crs = crs; + } + + @Override + public void append( String value ) + { + coordinates[index++] = value; + } + + @Override + public String done() + { + return format( + "{geometry: " + + "{type: \"Point\", coordinates: [%s, %s], crs: " + + "{type: link, " + + "properties: {href: \"%s\", code: %d}" + + "}" + + "}" + + "}", + coordinates[0], coordinates[1], crs.href(), crs.code() ); + } + } } diff --git a/community/values/src/test/java/org/neo4j/values/utils/PrettyPrinterTest.java b/community/values/src/test/java/org/neo4j/values/utils/PrettyPrinterTest.java index 68500b62cc5a7..900baed741cc9 100644 --- a/community/values/src/test/java/org/neo4j/values/utils/PrettyPrinterTest.java +++ b/community/values/src/test/java/org/neo4j/values/utils/PrettyPrinterTest.java @@ -1,3 +1,22 @@ +/* + * Copyright (c) 2002-2017 "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.values.utils; import org.junit.Test; @@ -6,6 +25,7 @@ import org.neo4j.values.AnyValue; import org.neo4j.values.storable.TextArray; +import org.neo4j.values.storable.TextValue; import org.neo4j.values.storable.Value; import org.neo4j.values.storable.Values; import org.neo4j.values.virtual.EdgeReference; @@ -54,7 +74,7 @@ public void shouldHandleNodeValue() node.writeTo( printer ); // Then - assertThat( printer.value(), equalTo( "(id=42 :L1:L2:L3 {bar: [1337, 'baz'], foo: 42})" ) ); + assertThat( printer.value(), equalTo( "(id=42 :L1:L2:L3 {bar: [1337, \"baz\"], foo: 42})" ) ); } @Test @@ -69,7 +89,7 @@ public void shouldHandleNodeValueWithoutLabels() node.writeTo( printer ); // Then - assertThat( printer.value(), equalTo( "(id=42 {bar: [1337, 'baz'], foo: 42})" ) ); + assertThat( printer.value(), equalTo( "(id=42 {bar: [1337, \"baz\"], foo: 42})" ) ); } @Test @@ -128,7 +148,7 @@ public void shouldHandleEdgeValue() edge.writeTo( printer ); // Then - assertThat( printer.value(), equalTo( "-[id=42 :R {bar: [1337, 'baz'], foo: 42}]-" ) ); + assertThat( printer.value(), equalTo( "-[id=42 :R {bar: [1337, \"baz\"], foo: 42}]-" ) ); } @Test @@ -202,7 +222,7 @@ public void shouldHandleLists() list.writeTo( printer ); // Then - assertThat( printer.value(), equalTo( "['foo', 42]" ) ); + assertThat( printer.value(), equalTo( "[\"foo\", 42]" ) ); } @Test @@ -216,7 +236,7 @@ public void shouldHandleArrays() array.writeTo( printer ); // Then - assertThat( printer.value(), equalTo( "['a', 'b', 'c']" ) ); + assertThat( printer.value(), equalTo( "[\"a\", \"b\", \"c\"]" ) ); } @Test @@ -271,9 +291,24 @@ public void shouldHandlePoints() pointValue.writeTo( printer ); // Then - assertThat( printer.value(), equalTo( "{geometry: {type: 'Point', coordinates: [11.0, 12.0], " + + assertThat( printer.value(), equalTo( "{geometry: {type: \"Point\", coordinates: [11.0, 12.0], " + "crs: {type: link, properties: " + - "{href: 'http://spatialreference.org/ref/sr-org/7203/', code: 7203}}}}" ) ); + "{href: \"http://spatialreference.org/ref/sr-org/7203/\", code: " + + "7203}}}}" ) ); + } + + @Test + public void shouldBeAbleToUseAnyQuoteMark() + { + // Given + TextValue hello = stringValue( "(ツ)" ); + PrettyPrinter printer = new PrettyPrinter( "__" ); + + // When + hello.writeTo( printer ); + + // Then + assertThat( printer.value(), equalTo( "__(ツ)__" ) ); } private MapValue props( Object... keyValue ) @@ -293,6 +328,4 @@ private MapValue props( Object... keyValue ) } return VirtualValues.map( map ); } - - -} \ No newline at end of file +} diff --git a/enterprise/cypher/slotted-runtime/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/slotted/pipes/TopSlottedPipeTest.scala b/enterprise/cypher/slotted-runtime/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/slotted/pipes/TopSlottedPipeTest.scala index 0025f3675b3b6..7670f8e80c700 100644 --- a/enterprise/cypher/slotted-runtime/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/slotted/pipes/TopSlottedPipeTest.scala +++ b/enterprise/cypher/slotted-runtime/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/slotted/pipes/TopSlottedPipeTest.scala @@ -27,7 +27,7 @@ import org.neo4j.cypher.internal.compatibility.v3_3.runtime.slotted.pipes.TopSlo import org.neo4j.cypher.internal.compatibility.v3_3.runtime.{LongSlot, PipelineInformation, RefSlot} import org.neo4j.cypher.internal.frontend.v3_3.symbols._ import org.neo4j.cypher.internal.frontend.v3_3.test_helpers.CypherFunSuite -import org.neo4j.cypher.internal.javacompat.ValueUtils +import org.neo4j.helpers.ValueUtils import scala.util.Random diff --git a/enterprise/query-logging/src/main/java/org/neo4j/kernel/impl/query/QueryLogFormatter.java b/enterprise/query-logging/src/main/java/org/neo4j/kernel/impl/query/QueryLogFormatter.java index b2c8a868d62f1..9875f05ec6df4 100644 --- a/enterprise/query-logging/src/main/java/org/neo4j/kernel/impl/query/QueryLogFormatter.java +++ b/enterprise/query-logging/src/main/java/org/neo4j/kernel/impl/query/QueryLogFormatter.java @@ -26,6 +26,7 @@ import org.neo4j.helpers.Strings; import org.neo4j.kernel.api.query.QuerySnapshot; import org.neo4j.values.AnyValue; +import org.neo4j.values.utils.PrettyPrinter; import org.neo4j.values.virtual.MapValue; class QueryLogFormatter @@ -85,7 +86,7 @@ static void formatMapValue( StringBuilder result, MapValue params, Collection params ) { formatMap( result, params, Collections.emptySet() ); diff --git a/enterprise/query-logging/src/test/java/org/neo4j/kernel/impl/query/QueryLoggerIT.java b/enterprise/query-logging/src/test/java/org/neo4j/kernel/impl/query/QueryLoggerIT.java index b21c9585fbba6..6cdcbe163f2bf 100644 --- a/enterprise/query-logging/src/test/java/org/neo4j/kernel/impl/query/QueryLoggerIT.java +++ b/enterprise/query-logging/src/test/java/org/neo4j/kernel/impl/query/QueryLoggerIT.java @@ -228,7 +228,7 @@ public void shouldLogParametersWhenNestedMap() throws Exception List logLines = readAllLines( logFilename ); assertEquals( 1, logLines.size() ); assertThat( logLines.get( 0 ), endsWith( String.format( - " ms: %s - %s - {props: {name: 'Roland', position: 'Gunslinger', followers: [Jake, Eddie, Susannah]}}" + " ms: %s - %s - {props: {name: 'Roland', position: 'Gunslinger', followers: ['Jake', 'Eddie', 'Susannah']}}" + " - {}", clientConnectionInfo(), query ) ) );