From 4e394ac13e816b45dcff31a2fc6ce5459ac405a3 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Tue, 17 Apr 2018 14:55:51 +0100 Subject: [PATCH] Escape quotation marks in Cypher strings --- .../okapi/api/value/CypherValue.scala | 20 ++++++++++++------- .../api/value/CAPSValueToStringTest.scala | 14 ++++++++++--- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/okapi-api/src/main/scala/org/opencypher/okapi/api/value/CypherValue.scala b/okapi-api/src/main/scala/org/opencypher/okapi/api/value/CypherValue.scala index 670330f19..ff8d90cc0 100644 --- a/okapi-api/src/main/scala/org/opencypher/okapi/api/value/CypherValue.scala +++ b/okapi-api/src/main/scala/org/opencypher/okapi/api/value/CypherValue.scala @@ -156,24 +156,24 @@ object CypherValue { */ def toCypherString: String = { this match { - case CypherString(s) => s"'$s'" + case CypherString(s) => s"'${escape(s)}'" case CypherList(l) => l.map(_.toCypherString).mkString("[", ", ", "]") case CypherMap(m) => m.toSeq .sortBy(_._1) .map { case (k, v) => s"$k: ${v.toCypherString}" } .mkString("{", ", ", "}") - case CypherRelationship(_, _, _, relType, CypherMap(properties)) => + case CypherRelationship(_, _, _, relType, props) => s"[:$relType${ - if (properties.isEmpty) "" - else s" ${properties.toCypherString}" + if (props.isEmpty) "" + else s" ${props.toCypherString}" }]" - case CypherNode(_, labels, CypherMap(properties)) => + case CypherNode(_, labels, props) => val labelString = if (labels.isEmpty) "" else labels.toSeq.sorted.mkString(":", ":", "") - val propertyString = if (properties.isEmpty) "" - else s"${properties.toCypherString}" + val propertyString = if (props.isEmpty) "" + else s"${props.toCypherString}" Seq(labelString, propertyString) .filter(_.nonEmpty) .mkString("(", " ", ")") @@ -181,6 +181,10 @@ object CypherValue { } } + private def escape(str: String): String = { + str.replaceAllLiterally("'", "\\'").replaceAllLiterally("\"", "\\\"") + } + private[okapi] def isOrContainsNull: Boolean = isNull || { this match { case l: CypherList => l.value.exists(_.isOrContainsNull) @@ -212,6 +216,8 @@ object CypherValue { implicit class CypherMap(val value: Map[String, CypherValue]) extends AnyVal with MaterialCypherValue[Map[String, CypherValue]] { override def unwrap: Map[String, Any] = value.map { case (k, v) => k -> v.unwrap } + def isEmpty: Boolean = value.isEmpty + def keys: Set[String] = value.keySet def get(k: String): Option[CypherValue] = value.get(k) diff --git a/spark-cypher/src/test/scala/org/opencypher/spark/api/value/CAPSValueToStringTest.scala b/spark-cypher/src/test/scala/org/opencypher/spark/api/value/CAPSValueToStringTest.scala index 5e895748e..c6a99b8cf 100644 --- a/spark-cypher/src/test/scala/org/opencypher/spark/api/value/CAPSValueToStringTest.scala +++ b/spark-cypher/src/test/scala/org/opencypher/spark/api/value/CAPSValueToStringTest.scala @@ -24,12 +24,13 @@ * described as "implementation extensions to Cypher" or as "proposed changes to * Cypher that are not yet approved by the openCypher community". */ -package org.opencypher.okapi.api.value +package org.opencypher.spark.api.value import org.opencypher.okapi.api.value.CypherValue._ -import org.opencypher.spark.test.CAPSTestSuite +import org.opencypher.okapi.api.value.{CAPSNode, CAPSRelationship} +import org.opencypher.okapi.test.BaseTestSuite -class CAPSValueToStringTest extends CAPSTestSuite { +class CAPSValueToStringTest extends BaseTestSuite { test("node") { CAPSNode(1L, Set.empty, CypherMap.empty).toCypherString should equal("()") @@ -64,4 +65,11 @@ class CAPSValueToStringTest extends CAPSTestSuite { CypherMap("a" -> 1).toCypherString should equal("{a: 1}") CypherMap("a" -> 1, "b" -> true).toCypherString should equal("{a: 1, b: true}") } + + test("should escape apostrophes in strings") { + CypherMap("street" -> "59 rue de l'Abbaye").toCypherString should equal("{street: '59 rue de l\\'Abbaye'}") + CypherMap("street" -> "59 rue de l\"Abbaye'").toCypherString should equal("{street: '59 rue de l\\\"Abbaye\\''}") + CAPSNode(1, Set.empty, CypherMap("street" -> "59 rue de l\"Abbaye'")).toCypherString should equal("({street: '59 rue de l\\\"Abbaye\\''})") + CAPSRelationship(1, 2, 3, "FOO", CypherMap("street" -> "59 rue de l\"Abbaye'")).toCypherString should equal("[:FOO {street: '59 rue de l\\\"Abbaye\\''}]") + } }