diff --git a/core/src/main/java/apoc/atomic/Atomic.java b/core/src/main/java/apoc/atomic/Atomic.java index 79dc86cc4..c60b57c31 100644 --- a/core/src/main/java/apoc/atomic/Atomic.java +++ b/core/src/main/java/apoc/atomic/Atomic.java @@ -203,7 +203,7 @@ public Stream update(@Name("container") Object nodeOrRelationship retry(executionContext, (context) -> { oldValue[0] = entity.getProperty(property); - String statement = "WITH $container as n with n set n." + property + "=" + operation + ";"; + String statement = "WITH $container as n with n set n." + Util.sanitize(property, true) + "=" + operation + ";"; Map properties = MapUtil.map("container", entity); return context.tx.execute(statement, properties); }, times); diff --git a/core/src/test/java/apoc/atomic/AtomicTest.java b/core/src/test/java/apoc/atomic/AtomicTest.java index b6217700a..28c2089b0 100644 --- a/core/src/test/java/apoc/atomic/AtomicTest.java +++ b/core/src/test/java/apoc/atomic/AtomicTest.java @@ -23,6 +23,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.hamcrest.Matchers; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -32,6 +33,7 @@ import org.neo4j.test.rule.DbmsRule; import org.neo4j.test.rule.ImpermanentDbmsRule; +import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -392,4 +394,62 @@ public void testConcurrentUpdate() throws Exception { long salary = TestUtil.singleResultFirstColumn(db, "MATCH (n:Person {name:'Tom'}) RETURN n.salary1 as salary;"); assertEquals(100L, salary); } + + @Test + public void testPropertyNamesWithSpecialCharacters(){ + db.executeTransactionally(""" + CREATE (p:Person { + `person.name`:'Tom', + `person.age`: 1, + `person.friends`: ["Fred", "George"], + `person.nickname`: 'Tom' + }) + """); + + String match = "MATCH (n:Person {`person.name`:'Tom'})"; + String returnStmt = "YIELD oldValue, newValue RETURN oldValue, newValue"; + + // ADD + TestUtil.testCall( + db, + match + " CALL apoc.atomic.add(n, 'person.age', 1) " + returnStmt, (r) -> { + Assert.assertEquals(1L, r.get("oldValue")); + Assert.assertEquals(2L, r.get("newValue")); + }); + // SUBTRACT + TestUtil.testCall( + db, + match + " CALL apoc.atomic.subtract(n,'person.age', 1) " + returnStmt, (r) -> { + Assert.assertEquals(2L, r.get("oldValue")); + Assert.assertEquals(1L, r.get("newValue")); + }); + // CONCAT + TestUtil.testCall( + db, + match + " CALL apoc.atomic.concat(n,'person.nickname', \"my\") "+ returnStmt, (r) -> { + Assert.assertEquals("Tom", r.get("oldValue")); + Assert.assertEquals("Tommy", r.get("newValue")); + }); + // INSERT + TestUtil.testCall( + db, + match + " CALL apoc.atomic.insert(n,'person.friends', 1, \"Ron\") " + returnStmt, (r) -> { + assertArrayEquals(new String[]{"Fred", "George"},(String[]) r.get("oldValue")); + assertArrayEquals(new String[]{"Fred", "Ron", "George"},(String[]) r.get("newValue")); + }); + // REMOVE + TestUtil.testCall( + db, + match + " CALL apoc.atomic.remove(n,'person.friends', 1) " + returnStmt, (r) -> { + assertEquals(List.of("Fred", "Ron", "George"), r.get("oldValue")); + assertArrayEquals(new String[]{"Fred", "George"},(String[]) r.get("newValue")); + }); + // UPDATE + TestUtil.testCall( + db, + match + " CALL apoc.atomic.update(n,'person.age','n.`person.age` * 3') " + returnStmt, (r) -> { + Assert.assertEquals(1L, r.get("oldValue")); + Assert.assertEquals(3L, r.get("newValue")); + }); + } }