Skip to content

Commit

Permalink
Fixes #2605: The apoc.custom.declareProcedure throws exception "Query…
Browse files Browse the repository at this point in the history
… results do not match requested output" with some queries (#3146) (#3149)
  • Loading branch information
vga91 authored Dec 14, 2022
1 parent 1673f5c commit 8d1fc5e
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 2 deletions.
9 changes: 7 additions & 2 deletions extended/src/main/java/apoc/custom/CypherProcedures.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,12 @@ private void validateProcedure(String statement, List<FieldSignature> input, Lis
(map, value) -> map.put(value.name(), null), HashMap::putAll),
result -> {
if (!DEFAULT_MAP_OUTPUT.equals(output)) {
checkOutputParams(outputSet, result.columns());
// when there are multiple variables with the same name, e.g within an "UNION ALL" Neo4j adds a suffix "@<number>" to distinguish them,
// so to check the correctness of the output parameters we must first remove this suffix from the column names
final Set<String> columns = result.columns().stream()
.map(i -> i.replaceFirst("@[0-9]+", "").trim())
.collect(Collectors.toSet());
checkOutputParams(outputSet, columns);
}
if (!DEFAULT_INPUTS.equals(input)) {
checkInputParams(result);
Expand All @@ -173,7 +178,7 @@ private void checkMode(QueryExecutionType.QueryType queryType, Mode mode) {
}


private void checkOutputParams(Set<String> outputSet, List<String> columns) {
private void checkOutputParams(Set<String> outputSet, Set<String> columns) {
if (!Set.copyOf(columns).equals(outputSet)) {
throw new RuntimeException(ERROR_MISMATCHED_OUTPUTS);
}
Expand Down
33 changes: 33 additions & 0 deletions extended/src/test/java/apoc/custom/CypherProceduresTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,39 @@ public void shouldRemovalOfFunctionNodeDeactivate() {
// when
TestUtil.singleResultFirstColumn(db, "return custom.answer()");
}

@Test
public void testIssue2605() {
db.executeTransactionally("CREATE (n:Test {id: 1})-[:has]->(:Log), (n)-[:has]->(:System)");
String query = "MATCH (node:Test)-[:has]->(log:Log) WHERE node.id = $id WITH node \n" +
"MATCH (node)-[:has]->(log:System) RETURN log, node";
db.executeTransactionally("CALL apoc.custom.declareProcedure('testIssue2605(id :: INTEGER ) :: (log :: NODE, node :: NODE)', $query, 'read')", Map.of("query", query));

// check query
TestUtil.testCall(db, "call custom.testIssue2605(1)", (row) -> {
assertEquals(List.of(Label.label("Test")), ((Node) row.get("node")).getLabels());
assertEquals(List.of(Label.label("System")), ((Node) row.get("log")).getLabels());
});

// UNION ALL github issue case
db.executeTransactionally("CREATE (n:ExampleNode {id: 1}), (:OtherExampleNode {identifier: '1'})");
String query2 = "MATCH (:ExampleNode)\n" +
" OPTIONAL MATCH (o:OtherExampleNode {identifier:$exampleId})\n" +
" RETURN o.identifier as value\n" +
" UNION ALL\n" +
" MATCH (n:ExampleNode)\n" +
" OPTIONAL MATCH (o:OtherExampleNode {identifier:$exampleId})\n" +
" RETURN o.identifier as value";
db.executeTransactionally("CALL apoc.custom.declareProcedure('exampleTest(exampleId::STRING) ::(value::STRING)', $query, 'read')", Map.of("query", query2));

// check query
final String identifier = "1";
TestUtil.testResult(db, "call custom.exampleTest($id)", Map.of("id", identifier), (r) -> {
assertEquals(identifier, r.next().get("value"));
assertEquals(identifier, r.next().get("value"));
assertFalse(r.hasNext());
});
}

@Test
public void shouldFailDeclareFunctionWithDefaultNumberParameters() {
Expand Down

0 comments on commit 8d1fc5e

Please sign in to comment.