From d222607cd6407fb9461c59efdedd5f05be245af3 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sat, 10 Oct 2026 14:24:25 +0200 Subject: [PATCH] Check the window aggregates on the typed Spark surface GeneratedSqlSurfaceTest.windowAggregatesAnswerWhatMobilityDBAnswers checks that wCount, wMax and wAvg over two temporal integers and wSum over a discrete temporal float, with a window of one day, answer what MobilityDB answers over values in partitions of their own, so the interval reaches the transition of every partition and the combine joins their states. Witness: with the generator of JMEOS ef197a02 the surface registers no window aggregate, the 30 overloads of wCount, wMin, wMax, wSum and wAvg left out as taking two arguments. Measured over MobilityDB 1f41894b35, the catalog of MEOS-API ff5f838173 and the generator of JMEOS #169 (26044b19): the 42 tests pass. Why: a window aggregate answers on Spark as it does in PostgreSQL. --- .../spark/GeneratedSqlSurfaceTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/java/org/mobilitydb/spark/GeneratedSqlSurfaceTest.java b/src/test/java/org/mobilitydb/spark/GeneratedSqlSurfaceTest.java index 666f2c74..8519b0be 100644 --- a/src/test/java/org/mobilitydb/spark/GeneratedSqlSurfaceTest.java +++ b/src/test/java/org/mobilitydb/spark/GeneratedSqlSurfaceTest.java @@ -306,6 +306,37 @@ void aggregatesOfEveryStateAnswerWhatMobilityDBAnswers() { "[3@2001-01-02 00:00:00+00, 5@2001-01-04 00:00:00+00]")); } + /** The text of a window aggregate over the values and a window of one day, each value in a + * partition of its own, as #aggregate reads an aggregate of one argument. */ + private static String windowAggregate(String agg, String in, String... values) { + StringBuilder rows = new StringBuilder(); + for (String v : values) { + rows.append(rows.length() == 0 ? "" : ", ").append("('").append(v).append("')"); + } + return (String) scalar("SELECT asText(" + agg + "(v, INTERVAL '1' DAY)) FROM (SELECT /*+ REPARTITION(" + + values.length + ") */ " + in + "(s) AS v FROM VALUES " + rows + " AS t(s))"); + } + + @Test + void windowAggregatesAnswerWhatMobilityDBAnswers() { + // What MobilityDB answers for the same window aggregate over the same values and a window + // of one day with the time zone set to UTC, each value in a partition of its own, so the + // interval reaches the transition of every partition and the combine joins their states. + String[] tints = {"[1@2001-01-01 00:00:00+00, 2@2001-01-03 00:00:00+00]", + "[3@2001-01-02 00:00:00+00, 4@2001-01-04 00:00:00+00]"}; + assertEquals("{[1@2001-01-01 00:00:00+00, 2@2001-01-02 00:00:00+00, 2@2001-01-04 00:00:00+00], " + + "(1@2001-01-04 00:00:00+00, 1@2001-01-05 00:00:00+00]}", + windowAggregate("wCount", "tintFromText", tints)); + assertEquals("{[1@2001-01-01 00:00:00+00, 3@2001-01-02 00:00:00+00, 3@2001-01-05 00:00:00+00]}", + windowAggregate("wMax", "tintFromText", tints)); + assertEquals("Interp=Step;{[1@2001-01-01 00:00:00+00, 2@2001-01-02 00:00:00+00, " + + "2@2001-01-04 00:00:00+00], (3@2001-01-04 00:00:00+00, 3@2001-01-05 00:00:00+00]}", + windowAggregate("wAvg", "tintFromText", tints)); + assertEquals("{[1@2001-01-01 00:00:00+00, 1@2001-01-02 00:00:00+00], " + + "[3@2001-01-03 00:00:00+00, 3@2001-01-04 00:00:00+00]}", + windowAggregate("wSum", "tfloatFromText", "{1@2001-01-01 00:00:00+00, 3@2001-01-03 00:00:00+00}")); + } + @Test void peakCountOverMergedPieces() { // The pieces of one trip are merged first, so the count counts trips, not pieces: two