diff --git a/README.md b/README.md index 63b0f51..6c0e1d6 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,9 @@ SparkSession spark = SparkSession.builder().appName("mobilityspark").master("loc GeneratedSpatioTemporalUDFs.registerAll(spark); ``` -Temporal values travel as strings in MEOS hex-WKB, and geometries as hex-EWKB, so any Spark type -system carries them: +Temporal values, spans, span sets and sets travel as their MEOS WKB bytes, a binary column, and a +column of their hex-WKB text reads as well; geometries travel as WKT or EWKT text, and boxes, +circular buffers, network points and poses as their text form: ```sql -- accessors under their canonical MobilityDB SQL names @@ -140,8 +141,8 @@ mvn -B clean test ``` `GeneratedSurfaceTest` drives the generated surface from known hex-WKB literals and asserts that -it *binds and executes*, not merely that it compiles: scalar accessors and I/O round-trips, double -/ boolean / byte marshalling, the cbuffer and npoint families, the JSON-path surface, value-array +it *binds and executes*, not merely that it compiles: scalar accessors and I/O round-trips, the +WKB bytes the functions return and the hex-WKB text they read, double / boolean / byte marshalling, the cbuffer and npoint families, the JSON-path surface, value-array accessors, the N-by-N array UDFs, the canonical `@sqlfn` names with runtime argument-kind dispatch, a folded out-parameter, and the H3 cell prefilter. The operator names are read from the catalog's own `byOperator` and `positionNames` maps rather than hard-coded, so a dialect rename diff --git a/src/main/java/org/mobilitydb/spark/MeosMemory.java b/src/main/java/org/mobilitydb/spark/MeosMemory.java index ef62f8c..c62d480 100644 --- a/src/main/java/org/mobilitydb/spark/MeosMemory.java +++ b/src/main/java/org/mobilitydb/spark/MeosMemory.java @@ -47,7 +47,7 @@ * * Usage: *
- *   Pointer tptr = GeneratedFunctions.temporal_from_hexwkb(hex);
+ *   Pointer tptr = GeneratedFunctions.temporal_from_wkb(wkb);
  *   try {
  *       // ... use tptr ...
  *   } finally {
diff --git a/src/test/java/org/mobilitydb/spark/GeneratedSurfaceTest.java b/src/test/java/org/mobilitydb/spark/GeneratedSurfaceTest.java
index 8810b2b..844d4b9 100644
--- a/src/test/java/org/mobilitydb/spark/GeneratedSurfaceTest.java
+++ b/src/test/java/org/mobilitydb/spark/GeneratedSurfaceTest.java
@@ -204,10 +204,10 @@ void portable_bare_name_dispatch_surface() {
         assertEquals(Boolean.TRUE, scalar(
             "SELECT " + position("&<#", "tbox") + "('" + TINT_HEX + "', '" + TINT_HEX + "')"));
         // temporal comparison (#=): of a value with itself is a temporal bool, carried as
-        // hex-WKB, not the boolean of the traditional =
+        // WKB, not the boolean of the traditional =
         Object teq = scalar("SELECT " + op("#=") + "('" + TINT_HEX + "', '" + TINT_HEX + "')");
         assertNotNull(teq);
-        assertTrue(teq instanceof String, "#= answers a temporal boolean, not " + teq.getClass());
+        assertTrue(teq instanceof byte[], "#= answers a temporal boolean, not " + teq.getClass());
         // ever comparison (?=): same value → true
         assertEquals(Boolean.TRUE, scalar(
             "SELECT " + op("?=") + "('" + TINT_HEX + "', '" + TINT_HEX + "')"));
@@ -275,6 +275,30 @@ void as_hexwkb_family_with_swallowed_size_out_param() {
             "SELECT temporal_num_instants(temporal_from_hexwkb('" + hex + "'))")).intValue());
     }
 
+    @Test
+    void values_travel_as_wkb_and_hex_wkb_stays_readable() {
+        String tint = "tint_in('[1@2001-01-01, 2@2001-01-02, 1@2001-01-03]')";
+        // A temporal, and the span a function derives from it, travel as their WKB bytes.
+        Object t = scalar("SELECT " + tint);
+        assertTrue(t instanceof byte[], "a temporal travels as WKB, got " + t.getClass());
+        Object span = scalar("SELECT timeSpan(" + tint + ")");
+        assertTrue(span instanceof byte[], "a span travels as WKB, got " + span.getClass());
+        // The bytes and the hex-WKB text of one value answer alike.
+        assertEquals(3, ((Number) scalar("SELECT numInstants(" + tint + ")")).intValue());
+        assertEquals(3, ((Number) scalar("SELECT numInstants('" + TINT_HEX + "')")).intValue());
+        // Hex-WKB in the big-endian byte order (WKB_EXTENDED | WKB_XDR = 20) is read by its
+        // own order, the type standing in the second and third bytes.
+        assertEquals(3, ((Number) scalar("SELECT numInstants(temporal_as_hexwkb("
+            + tint + ", CAST(20 AS BYTE)))")).intValue());
+        // An aggregate over a column of hex-WKB text answers what it answers over the WKB
+        // of the same value.
+        Object overWkb = scalar("SELECT tint_out(tCount(v)) FROM (SELECT temporal_from_hexwkb('"
+            + TINT_HEX + "') AS v)");
+        assertNotNull(overWkb);
+        assertEquals(overWkb,
+            scalar("SELECT tint_out(tCount(s)) FROM VALUES ('" + TINT_HEX + "') AS t(s)"));
+    }
+
     @Test
     void bool_result_value_at_timestamptz_folds_the_value_out_param() {
         // tint_value_at_timestamptz(temp, t, strict, int *value) returns bool and writes