@@ -301,4 +301,74 @@ void callSubqueryWithUnionFallsThrough() {
301301 assertThat (((Number ) row2 .getProperty ("success2" )).intValue ()).isEqualTo (2 );
302302 assertThat (result2 .hasNext ()).isFalse ();
303303 }
304+
305+ /**
306+ * Issue #3944: Unit CALL subquery (no RETURN) with inner UNWIND must not multiply outer rows.
307+ * Each outer row should appear exactly once regardless of inner UNWIND cardinality.
308+ */
309+ @ Test
310+ void unitCallSubqueryWithUnwindPreservesOuterRowCount () {
311+ database .getSchema ().createVertexType ("Person3944" );
312+ database .getSchema ().createVertexType ("Clone3944" );
313+
314+ database .transaction (() -> {
315+ database .command ("opencypher" , "CREATE (:Person3944 {name: 'Alice'})" );
316+ database .command ("opencypher" , "CREATE (:Person3944 {name: 'Bob'})" );
317+ database .command ("opencypher" , "CREATE (:Person3944 {name: 'Charlie'})" );
318+ });
319+
320+ database .transaction (() -> {
321+ final ResultSet result = database .command ("opencypher" ,
322+ "MATCH (p:Person3944) " +
323+ "CALL { " +
324+ " WITH p " +
325+ " UNWIND range(1, 2) AS i " +
326+ " CREATE (:Clone3944 {name: p.name, id: i}) " +
327+ "} " +
328+ "RETURN p.name AS person " +
329+ "ORDER BY person" );
330+
331+ final List <Result > rows = new ArrayList <>();
332+ while (result .hasNext ())
333+ rows .add (result .next ());
334+
335+ assertThat (rows ).as ("Unit CALL subquery must not multiply outer rows by inner UNWIND cardinality" ).hasSize (3 );
336+ assertThat ((String ) rows .get (0 ).getProperty ("person" )).isEqualTo ("Alice" );
337+ assertThat ((String ) rows .get (1 ).getProperty ("person" )).isEqualTo ("Bob" );
338+ assertThat ((String ) rows .get (2 ).getProperty ("person" )).isEqualTo ("Charlie" );
339+ });
340+ }
341+
342+ /**
343+ * Issue #3944 control: Unit CALL subquery without UNWIND should still work correctly (one row per outer row).
344+ */
345+ @ Test
346+ void unitCallSubqueryWithoutUnwindPreservesOuterRowCount () {
347+ database .getSchema ().createVertexType ("Person3944b" );
348+ database .getSchema ().createVertexType ("Clone3944b" );
349+
350+ database .transaction (() -> {
351+ database .command ("opencypher" , "CREATE (:Person3944b {name: 'Alice'})" );
352+ database .command ("opencypher" , "CREATE (:Person3944b {name: 'Bob'})" );
353+ });
354+
355+ database .transaction (() -> {
356+ final ResultSet result = database .command ("opencypher" ,
357+ "MATCH (p:Person3944b) " +
358+ "CALL { " +
359+ " WITH p " +
360+ " CREATE (:Clone3944b {name: p.name}) " +
361+ "} " +
362+ "RETURN p.name AS person " +
363+ "ORDER BY person" );
364+
365+ final List <Result > rows = new ArrayList <>();
366+ while (result .hasNext ())
367+ rows .add (result .next ());
368+
369+ assertThat (rows ).as ("Unit CALL subquery must produce exactly one row per outer row" ).hasSize (2 );
370+ assertThat ((String ) rows .get (0 ).getProperty ("person" )).isEqualTo ("Alice" );
371+ assertThat ((String ) rows .get (1 ).getProperty ("person" )).isEqualTo ("Bob" );
372+ });
373+ }
304374}
0 commit comments