Skip to content

Commit eeb54cd

Browse files
committed
GH-2681 - Sanitize label replacements.
This requires an update of the CypherDSL to use the built-in function instead of copying code.
1 parent af86398 commit eeb54cd

File tree

3 files changed

+28
-16
lines changed

3 files changed

+28
-16
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
<byte-buddy.version>1.11.0</byte-buddy.version>
7676
<cdi>2.0.SP1</cdi>
7777
<checkstyle.version>8.40</checkstyle.version>
78-
<cypher-dsl.version>2022.4.0</cypher-dsl.version>
78+
<cypher-dsl.version>2022.8.5</cypher-dsl.version>
7979
<dist.id>spring-data-neo4j</dist.id>
8080
<dist.key>SDNEO4J</dist.key>
8181
<flatten-maven-plugin.version>1.2.5</flatten-maven-plugin.version>

src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.neo4j.cypherdsl.core.Conditions;
4040
import org.neo4j.cypherdsl.core.Cypher;
4141
import org.neo4j.cypherdsl.core.Expression;
42+
import org.neo4j.cypherdsl.core.FunctionInvocation;
4243
import org.neo4j.cypherdsl.core.Functions;
4344
import org.neo4j.cypherdsl.core.MapProjection;
4445
import org.neo4j.cypherdsl.core.Node;
@@ -54,6 +55,7 @@
5455
import org.neo4j.cypherdsl.core.SymbolicName;
5556
import org.neo4j.cypherdsl.core.renderer.Configuration;
5657
import org.neo4j.cypherdsl.core.renderer.Renderer;
58+
import org.neo4j.cypherdsl.core.utils.Assertions;
5759
import org.springframework.data.domain.Sort;
5860
import org.springframework.data.mapping.MappingException;
5961
import org.springframework.data.mapping.PersistentProperty;
@@ -62,6 +64,8 @@
6264
import org.springframework.lang.Nullable;
6365
import org.springframework.util.Assert;
6466

67+
import javax.lang.model.SourceVersion;
68+
6569
/**
6670
* A generator based on the schema defined by node and relationship descriptions. Most methods return renderable Cypher
6771
* statements.
@@ -348,7 +352,7 @@ public Statement prepareSaveOf(NodeDescription<?> nodeDescription,
348352
Property versionProperty = rootNode.property(((Neo4jPersistentEntity<?>) nodeDescription).getRequiredVersionProperty().getName());
349353

350354
createIfNew = updateDecorator.apply(optionalMatch(possibleExistingNode)
351-
.where(possibleExistingNode.internalId().isEqualTo(idParameter))
355+
.where(idFunction(possibleExistingNode).isEqualTo(idParameter))
352356
.with(possibleExistingNode)
353357
.where(possibleExistingNode.isNull())
354358
.create(rootNode.withProperties(versionProperty, literalOf(0)))
@@ -358,7 +362,7 @@ public Statement prepareSaveOf(NodeDescription<?> nodeDescription,
358362
.build();
359363

360364
updateIfExists = updateDecorator.apply(match(rootNode)
361-
.where(rootNode.internalId().isEqualTo(idParameter))
365+
.where(idFunction(rootNode).isEqualTo(idParameter))
362366
.and(versionProperty.isEqualTo(parameter(Constants.NAME_OF_VERSION_PARAM))) // Initial check
363367
.set(versionProperty.to(versionProperty.add(literalOf(1)))) // Acquire lock
364368
.with(rootNode)
@@ -368,19 +372,23 @@ public Statement prepareSaveOf(NodeDescription<?> nodeDescription,
368372
.returning(rootNode).build();
369373
} else {
370374
createIfNew = updateDecorator
371-
.apply(optionalMatch(possibleExistingNode).where(possibleExistingNode.internalId().isEqualTo(idParameter))
375+
.apply(optionalMatch(possibleExistingNode).where(idFunction(possibleExistingNode).isEqualTo(idParameter))
372376
.with(possibleExistingNode).where(possibleExistingNode.isNull()).create(rootNode)
373377
.set(rootNode, parameter(Constants.NAME_OF_PROPERTIES_PARAM)))
374378
.returning(rootNode).build();
375379

376-
updateIfExists = updateDecorator.apply(match(rootNode).where(rootNode.internalId().isEqualTo(idParameter))
380+
updateIfExists = updateDecorator.apply(match(rootNode).where(idFunction(rootNode).isEqualTo(idParameter))
377381
.mutate(rootNode, parameter(Constants.NAME_OF_PROPERTIES_PARAM))).returning(rootNode).build();
378382
}
379383

380384
return Cypher.union(createIfNew, updateIfExists);
381385
}
382386
}
383387

388+
private static FunctionInvocation idFunction(Node rootNode) {
389+
return Functions.id(rootNode);
390+
}
391+
384392
public Statement prepareSaveOfMultipleInstancesOf(NodeDescription<?> nodeDescription) {
385393

386394
Assert.isTrue(!nodeDescription.isUsingInternalIds(),
@@ -398,7 +406,7 @@ public Statement prepareSaveOfMultipleInstancesOf(NodeDescription<?> nodeDescrip
398406
return Cypher.unwind(parameter(Constants.NAME_OF_ENTITY_LIST_PARAM)).as(row)
399407
.merge(rootNode.withProperties(nameOfIdProperty, Cypher.property(row, Constants.NAME_OF_ID)))
400408
.mutate(rootNode, Cypher.property(row, Constants.NAME_OF_PROPERTIES_PARAM))
401-
.returning(rootNode.internalId().as(Constants.NAME_OF_INTERNAL_ID), rootNode.property(nameOfIdProperty).as(Constants.NAME_OF_ID))
409+
.returning(idFunction(rootNode).as(Constants.NAME_OF_INTERNAL_ID), rootNode.property(nameOfIdProperty).as(Constants.NAME_OF_ID))
402410
.build();
403411
}
404412

@@ -420,9 +428,9 @@ public Statement prepareSaveOfRelationship(Neo4jPersistentEntity<?> neo4jPersist
420428
startNode.relationshipFrom(endNode, type)).named(RELATIONSHIP_NAME);
421429

422430
return match(startNode)
423-
.where(neo4jPersistentEntity.isUsingInternalIds() ? startNode.internalId().isEqualTo(idParameter)
431+
.where(neo4jPersistentEntity.isUsingInternalIds() ? idFunction(startNode).isEqualTo(idParameter)
424432
: startNode.property(idPropertyName).isEqualTo(idParameter))
425-
.match(endNode).where(endNode.internalId().isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)))
433+
.match(endNode).where(idFunction(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)))
426434
.merge(relationshipFragment)
427435
.returning(Functions.id(relationshipFragment))
428436
.build();
@@ -450,9 +458,9 @@ public Statement prepareSaveOfRelationships(Neo4jPersistentEntity<?> neo4jPersis
450458
.with(row)
451459
.match(startNode)
452460
.where(neo4jPersistentEntity.isUsingInternalIds()
453-
? startNode.internalId().isEqualTo(idProperty)
461+
? idFunction(startNode).isEqualTo(idProperty)
454462
: startNode.property(idPropertyName).isEqualTo(idProperty))
455-
.match(endNode).where(endNode.internalId().isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
463+
.match(endNode).where(idFunction(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
456464
.merge(relationshipFragment)
457465
.returning(Functions.id(relationshipFragment))
458466
.build();
@@ -482,9 +490,9 @@ public Statement prepareSaveOfRelationshipWithProperties(Neo4jPersistentEntity<?
482490
.named(RELATIONSHIP_NAME);
483491

484492
StatementBuilder.OngoingReadingWithWhere startAndEndNodeMatch = match(startNode)
485-
.where(neo4jPersistentEntity.isUsingInternalIds() ? startNode.internalId().isEqualTo(idParameter)
493+
.where(neo4jPersistentEntity.isUsingInternalIds() ? idFunction(startNode).isEqualTo(idParameter)
486494
: startNode.property(idPropertyName).isEqualTo(idParameter))
487-
.match(endNode).where(endNode.internalId().isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)));
495+
.match(endNode).where(idFunction(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)));
488496

489497
StatementBuilder.ExposesSet createOrMatch = isNew
490498
? startAndEndNodeMatch.create(relationshipFragment)
@@ -527,10 +535,10 @@ public Statement prepareUpdateOfRelationshipsWithProperties(Neo4jPersistentEntit
527535
.match(startNode)
528536
.where(
529537
neo4jPersistentEntity.isUsingInternalIds()
530-
? startNode.internalId().isEqualTo(idProperty)
538+
? idFunction(startNode).isEqualTo(idProperty)
531539
: startNode.property(idPropertyName).isEqualTo(idProperty)
532540
)
533-
.match(endNode).where(endNode.internalId().isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
541+
.match(endNode).where(idFunction(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
534542
.create(relationshipFragment)
535543
.mutate(RELATIONSHIP_NAME, relationshipProperties)
536544
.returning(Functions.id(relationshipFragment)).build();
@@ -566,7 +574,7 @@ public Statement prepareDeleteOf(
566574

567575
Parameter<?> idParameter = parameter(Constants.FROM_ID_PARAMETER_NAME);
568576
return match(relationship)
569-
.where(neo4jPersistentEntity.isUsingInternalIds() ? startNode.internalId().isEqualTo(idParameter)
577+
.where(neo4jPersistentEntity.isUsingInternalIds() ? idFunction(startNode).isEqualTo(idParameter)
570578
: startNode.property(idPropertyName).isEqualTo(idParameter))
571579
.and(Functions.id(relationship).in(Cypher.parameter(Constants.NAME_OF_KNOWN_RELATIONSHIPS_PARAM)).not())
572580
.delete(relationship.getRequiredSymbolicName())
@@ -613,6 +621,7 @@ public Collection<Expression> createReturnStatementForMatch(Neo4jPersistentEntit
613621
}
614622
expression = Cypher.property(property.substring(0, firstDot), tail);
615623
} else {
624+
Assertions.isTrue(SourceVersion.isIdentifier(property), "Name must be a valid identifier.");
616625
expression = Cypher.name(property);
617626
}
618627
if (order.isIgnoreCase()) {

src/main/java/org/springframework/data/neo4j/repository/query/Neo4jSpelSupport.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.apache.commons.logging.LogFactory;
2828
import org.apiguardian.api.API;
29+
import org.neo4j.cypherdsl.core.internal.SchemaNames;
2930
import org.springframework.core.log.LogAccessor;
3031
import org.springframework.data.domain.Pageable;
3132
import org.springframework.data.domain.Sort;
@@ -111,7 +112,9 @@ private static LiteralReplacement labels(@Nullable Object arg, String joinOn) {
111112

112113
private static String joinStrings(Object arg, String joinOn) {
113114
if (arg instanceof Collection) {
114-
return ((Collection<?>) arg).stream().map(Object::toString).collect(Collectors.joining(joinOn));
115+
return ((Collection<?>) arg).stream()
116+
.map(o -> SchemaNames.sanitize(o.toString()).get())
117+
.collect(Collectors.joining(joinOn));
115118
}
116119

117120
// we are so kind and also accept plain strings instead of collection<string>

0 commit comments

Comments
 (0)