From 14b577f9b8abfb0af60b121da4ec9d3026934fcd Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 28 May 2026 01:22:57 +0200 Subject: [PATCH] test(parity): audit and verify the MEOS facade surface per family Add a symbol-level parity audit measuring the generated MeosOps* facade against the MEOS public-header surface, the forwarding methods (MeosOpsParityGaps) that bring the facade to the full public surface, the parity-status report, and a JUnit smoke test that constructs and reads back a value in the core, geo, cbuffer, npoint and pose families through the facade against libmeos. (cherry picked from commit bc2b474e7dc1608b860f015c42e807f26ba1ede6) --- flink-processor/docs/parity-status.md | 43 ++ .../flink/meos/MeosOpsParityGaps.java | 454 ++++++++++++++++++ .../flink/meos/MeosFacadeSmokeTest.java | 79 +++ .../tools/parity/emit_gap_methods.py | 89 ++++ flink-processor/tools/parity/parity_audit.py | 194 ++++++++ 5 files changed, 859 insertions(+) create mode 100644 flink-processor/docs/parity-status.md create mode 100644 flink-processor/src/main/java/org/mobilitydb/flink/meos/MeosOpsParityGaps.java create mode 100644 flink-processor/src/test/java/org/mobilitydb/flink/meos/MeosFacadeSmokeTest.java create mode 100644 flink-processor/tools/parity/emit_gap_methods.py create mode 100644 flink-processor/tools/parity/parity_audit.py diff --git a/flink-processor/docs/parity-status.md b/flink-processor/docs/parity-status.md new file mode 100644 index 0000000..bf2650e --- /dev/null +++ b/flink-processor/docs/parity-status.md @@ -0,0 +1,43 @@ +# MobilityFlink parity status — MEOS surface audit + +Generated 2026-05-28 by `tools/parity/parity_audit.py`. + +The MobilityFlink MEOS facade (`org.mobilitydb.flink.meos.MeosOps*`) exposes MEOS C functions to Flink through JMEOS. This audit measures, per type family, the share of the **MEOS public C API** that the facade exposes and that JMEOS binds. + +**Headline.** The facade exposes **2156 of 2156 public MEOS functions (100.0%)**. The MEOS public surface (`meos/include/meos*.h`, excluding internal headers) is 2156 functions; JMEOS binds 2156 of them. 0 bindable functions are not exposed (listed in §3). + +Coverage is **static**: a function counts as covered when the facade declares a method of the same name and arity that delegates to a JMEOS export. + +Per-family runtime behaviour is asserted by `src/test/java/org/mobilitydb/flink/meos/MeosFacadeSmokeTest.java`, which constructs and reads back a value in the core, geo, cbuffer, npoint and pose families through the facade against libmeos. The cbuffer, npoint and pose families require a libmeos built with the extended modules (`-DCBUFFER=ON -DNPOINT=ON -DPOSE=ON -DRGEO=ON`); the stock library carries the core and geo surfaces only. + +## 1. Reference surface and method + +- **Denominator**: distinct function names declared `extern` in the MEOS public headers `meos.h`, `meos_geo.h`, `meos_cbuffer.h`, `meos_npoint.h`, `meos_pose.h`, `meos_rgeo.h`. Internal headers (`meos_internal*.h`) are excluded. + +- **Numerator**: `public static` methods on the generated `MeosOps*` facade whose name is also a `functions.GeneratedFunctions` export in the bundled JMEOS jar. + +- **JMEOS jar**: jar/JMEOS.jar exports 2699 static methods. + +## 2. Per-family coverage of the public MEOS surface + +| Family (header) | Public ∩ JMEOS | Exposed | Missing | Coverage | +|---|---:|---:|---:|---:| +| core temporal / set / span / spanset / tbox (`meos.h`) | 1279 | 1279 | 0 | 100.0% | +| geo (tgeo / tpoint / stbox) (`meos_geo.h`) | 417 | 417 | 0 | 100.0% | +| cbuffer (`meos_cbuffer.h`) | 173 | 173 | 0 | 100.0% | +| npoint (`meos_npoint.h`) | 118 | 118 | 0 | 100.0% | +| pose (`meos_pose.h`) | 101 | 101 | 0 | 100.0% | +| rgeo (`meos_rgeo.h`) | 68 | 68 | 0 | 100.0% | +| **total** | **2156** | **2156** | **0** | **100.0%** | + +## 3. Bindable MEOS functions not exposed by the facade + +0 functions are present in the public MEOS headers and bound by JMEOS but not generated into the facade: + + +## 4. MobilityDB SQL-surface cross-check + +For comparability with the SQL-surface bindings, the facade is also matched against the underlying MEOS C symbol of each addressable `CREATE FUNCTION` in `mobilitydb/sql/**/*.in.sql` (PG-only sections and helper symbols bucketed out; 874 out-of-scope, 113 SQL/plpgsql-composed functions with no single C symbol). The extension wrapper occasionally renames the MEOS function it calls, so this view is a lower bound. + +- Addressable distinct C symbols: **1335**; bound by JMEOS: **1065**; exposed by the facade: **939** (88.2% of the JMEOS-bindable SQL surface). + diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/MeosOpsParityGaps.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/MeosOpsParityGaps.java new file mode 100644 index 0000000..34a9d7b --- /dev/null +++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/MeosOpsParityGaps.java @@ -0,0 +1,454 @@ +package org.mobilitydb.flink.meos; + +/** + * Forwarding facade methods for MEOS public-surface functions not emitted + * by the tier-aware code generator. Each method delegates to its JMEOS + * {@code functions.GeneratedFunctions} export under the shared + * {@link MeosOpsRuntime#MEOS_AVAILABLE} guard. + */ +public final class MeosOpsParityGaps { + + private MeosOpsParityGaps() { /* utility */ } + + /** MEOS {@code bearing_tpoint_point} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer bearing_tpoint_point(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1, int arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("bearing_tpoint_point requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.bearing_tpoint_point(arg0, arg1, arg2); + } + + /** MEOS {@code bearing_tpoint_tpoint} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer bearing_tpoint_tpoint(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("bearing_tpoint_tpoint requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.bearing_tpoint_tpoint(arg0, arg1); + } + + /** MEOS {@code geogpoint_make2d} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer geogpoint_make2d(int arg0, double arg1, double arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("geogpoint_make2d requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.geogpoint_make2d(arg0, arg1, arg2); + } + + /** MEOS {@code geogpoint_make3dz} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer geogpoint_make3dz(int arg0, double arg1, double arg2, double arg3) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("geogpoint_make3dz requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.geogpoint_make3dz(arg0, arg1, arg2, arg3); + } + + /** MEOS {@code geomeas_to_tpoint} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer geomeas_to_tpoint(jnr.ffi.Pointer arg0) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("geomeas_to_tpoint requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.geomeas_to_tpoint(arg0); + } + + /** MEOS {@code geompoint_make2d} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer geompoint_make2d(int arg0, double arg1, double arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("geompoint_make2d requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.geompoint_make2d(arg0, arg1, arg2); + } + + /** MEOS {@code geompoint_make3dz} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer geompoint_make3dz(int arg0, double arg1, double arg2, double arg3) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("geompoint_make3dz requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.geompoint_make3dz(arg0, arg1, arg2, arg3); + } + + /** MEOS {@code geompoint_to_npoint} — meos_npoint.h · scalar / stateless. */ + public static jnr.ffi.Pointer geompoint_to_npoint(jnr.ffi.Pointer arg0) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("geompoint_to_npoint requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.geompoint_to_npoint(arg0); + } + + /** MEOS {@code intersection_cbuffer_set} — meos_cbuffer.h · scalar / stateless. */ + public static jnr.ffi.Pointer intersection_cbuffer_set(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("intersection_cbuffer_set requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.intersection_cbuffer_set(arg0, arg1); + } + + /** MEOS {@code intersection_npoint_set} — meos_npoint.h · scalar / stateless. */ + public static jnr.ffi.Pointer intersection_npoint_set(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("intersection_npoint_set requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.intersection_npoint_set(arg0, arg1); + } + + /** MEOS {@code intersection_pose_set} — meos_pose.h · scalar / stateless. */ + public static jnr.ffi.Pointer intersection_pose_set(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("intersection_pose_set requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.intersection_pose_set(arg0, arg1); + } + + /** MEOS {@code line_interpolate_point} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer line_interpolate_point(jnr.ffi.Pointer arg0, double arg1, int arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("line_interpolate_point requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.line_interpolate_point(arg0, arg1, arg2); + } + + /** MEOS {@code line_locate_point} — meos_geo.h · scalar / stateless. */ + public static double line_locate_point(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("line_locate_point requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.line_locate_point(arg0, arg1); + } + + /** MEOS {@code line_point_n} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer line_point_n(jnr.ffi.Pointer arg0, int arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("line_point_n requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.line_point_n(arg0, arg1); + } + + /** MEOS {@code line_substring} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer line_substring(jnr.ffi.Pointer arg0, double arg1, double arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("line_substring requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.line_substring(arg0, arg1, arg2); + } + + /** MEOS {@code mult_float_tfloat} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer mult_float_tfloat(double arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("mult_float_tfloat requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.mult_float_tfloat(arg0, arg1); + } + + /** MEOS {@code mult_int_tint} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer mult_int_tint(int arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("mult_int_tint requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.mult_int_tint(arg0, arg1); + } + + /** MEOS {@code mult_tfloat_float} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer mult_tfloat_float(jnr.ffi.Pointer arg0, double arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("mult_tfloat_float requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.mult_tfloat_float(arg0, arg1); + } + + /** MEOS {@code mult_tint_int} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer mult_tint_int(jnr.ffi.Pointer arg0, int arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("mult_tint_int requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.mult_tint_int(arg0, arg1); + } + + /** MEOS {@code mult_tnumber_tnumber} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer mult_tnumber_tnumber(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("mult_tnumber_tnumber requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.mult_tnumber_tnumber(arg0, arg1); + } + + /** MEOS {@code nsegment_end_position} — meos_npoint.h · scalar / stateless. */ + public static double nsegment_end_position(jnr.ffi.Pointer arg0) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("nsegment_end_position requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.nsegment_end_position(arg0); + } + + /** MEOS {@code nsegment_start_position} — meos_npoint.h · scalar / stateless. */ + public static double nsegment_start_position(jnr.ffi.Pointer arg0) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("nsegment_start_position requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.nsegment_start_position(arg0); + } + + /** MEOS {@code route_geom} — meos_npoint.h · scalar / stateless. */ + public static jnr.ffi.Pointer route_geom(int arg0) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("route_geom requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.route_geom(arg0); + } + + /** MEOS {@code spatialset_set_srid} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer spatialset_set_srid(jnr.ffi.Pointer arg0, int arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("spatialset_set_srid requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.spatialset_set_srid(arg0, arg1); + } + + /** MEOS {@code spatialset_transform} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer spatialset_transform(jnr.ffi.Pointer arg0, int arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("spatialset_transform requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.spatialset_transform(arg0, arg1); + } + + /** MEOS {@code spatialset_transform_pipeline} — meos_geo.h · scalar / stateless. */ + public static jnr.ffi.Pointer spatialset_transform_pipeline(jnr.ffi.Pointer arg0, java.lang.String arg1, int arg2, int arg3) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("spatialset_transform_pipeline requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.spatialset_transform_pipeline(arg0, arg1, arg2, arg3); + } + + /** MEOS {@code tand_bool_tbool} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer tand_bool_tbool(int arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tand_bool_tbool requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tand_bool_tbool(arg0, arg1); + } + + /** MEOS {@code tand_tbool_bool} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer tand_tbool_bool(jnr.ffi.Pointer arg0, int arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tand_tbool_bool requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tand_tbool_bool(arg0, arg1); + } + + /** MEOS {@code tand_tbool_tbool} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer tand_tbool_tbool(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tand_tbool_tbool requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tand_tbool_tbool(arg0, arg1); + } + + /** MEOS {@code tboolseq_from_base_tstzset} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tboolseq_from_base_tstzset(int arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tboolseq_from_base_tstzset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tboolseq_from_base_tstzset(arg0, arg1); + } + + /** MEOS {@code tboolseq_from_base_tstzspan} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tboolseq_from_base_tstzspan(int arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tboolseq_from_base_tstzspan requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tboolseq_from_base_tstzspan(arg0, arg1); + } + + /** MEOS {@code tboolseqset_from_base_tstzspanset} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tboolseqset_from_base_tstzspanset(int arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tboolseqset_from_base_tstzspanset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tboolseqset_from_base_tstzspanset(arg0, arg1); + } + + /** MEOS {@code tfloatbox_time_tiles} — meos.h · multidimensional tiling (windowed). */ + public static jnr.ffi.Pointer tfloatbox_time_tiles(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1, int arg2, jnr.ffi.Pointer arg3) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tfloatbox_time_tiles requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tfloatbox_time_tiles(arg0, arg1, arg2, arg3); + } + + /** MEOS {@code tfloatbox_value_tiles} — meos.h · multidimensional tiling (windowed). */ + public static jnr.ffi.Pointer tfloatbox_value_tiles(jnr.ffi.Pointer arg0, double arg1, double arg2, jnr.ffi.Pointer arg3) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tfloatbox_value_tiles requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tfloatbox_value_tiles(arg0, arg1, arg2, arg3); + } + + /** MEOS {@code tfloatbox_value_time_tiles} — meos.h · multidimensional tiling (windowed). */ + public static jnr.ffi.Pointer tfloatbox_value_time_tiles(jnr.ffi.Pointer arg0, double arg1, jnr.ffi.Pointer arg2, double arg3, int arg4, jnr.ffi.Pointer arg5) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tfloatbox_value_time_tiles requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tfloatbox_value_time_tiles(arg0, arg1, arg2, arg3, arg4, arg5); + } + + /** MEOS {@code tfloatseq_from_base_tstzset} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tfloatseq_from_base_tstzset(double arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tfloatseq_from_base_tstzset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tfloatseq_from_base_tstzset(arg0, arg1); + } + + /** MEOS {@code tfloatseq_from_base_tstzspan} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tfloatseq_from_base_tstzspan(double arg0, jnr.ffi.Pointer arg1, int arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tfloatseq_from_base_tstzspan requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tfloatseq_from_base_tstzspan(arg0, arg1, arg2); + } + + /** MEOS {@code tfloatseqset_from_base_tstzspanset} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tfloatseqset_from_base_tstzspanset(double arg0, jnr.ffi.Pointer arg1, int arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tfloatseqset_from_base_tstzspanset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tfloatseqset_from_base_tstzspanset(arg0, arg1, arg2); + } + + /** MEOS {@code tgeoseq_from_base_tstzset} — meos_geo.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tgeoseq_from_base_tstzset(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tgeoseq_from_base_tstzset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tgeoseq_from_base_tstzset(arg0, arg1); + } + + /** MEOS {@code tgeoseq_from_base_tstzspan} — meos_geo.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tgeoseq_from_base_tstzspan(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1, int arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tgeoseq_from_base_tstzspan requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tgeoseq_from_base_tstzspan(arg0, arg1, arg2); + } + + /** MEOS {@code tgeoseqset_from_base_tstzspanset} — meos_geo.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tgeoseqset_from_base_tstzspanset(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1, int arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tgeoseqset_from_base_tstzspanset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tgeoseqset_from_base_tstzspanset(arg0, arg1, arg2); + } + + /** MEOS {@code tintbox_time_tiles} — meos.h · multidimensional tiling (windowed). */ + public static jnr.ffi.Pointer tintbox_time_tiles(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1, int arg2, jnr.ffi.Pointer arg3) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tintbox_time_tiles requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tintbox_time_tiles(arg0, arg1, arg2, arg3); + } + + /** MEOS {@code tintbox_value_tiles} — meos.h · multidimensional tiling (windowed). */ + public static jnr.ffi.Pointer tintbox_value_tiles(jnr.ffi.Pointer arg0, int arg1, int arg2, jnr.ffi.Pointer arg3) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tintbox_value_tiles requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tintbox_value_tiles(arg0, arg1, arg2, arg3); + } + + /** MEOS {@code tintbox_value_time_tiles} — meos.h · multidimensional tiling (windowed). */ + public static jnr.ffi.Pointer tintbox_value_time_tiles(jnr.ffi.Pointer arg0, int arg1, jnr.ffi.Pointer arg2, int arg3, int arg4, jnr.ffi.Pointer arg5) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tintbox_value_time_tiles requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tintbox_value_time_tiles(arg0, arg1, arg2, arg3, arg4, arg5); + } + + /** MEOS {@code tintseq_from_base_tstzset} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tintseq_from_base_tstzset(int arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tintseq_from_base_tstzset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tintseq_from_base_tstzset(arg0, arg1); + } + + /** MEOS {@code tintseq_from_base_tstzspan} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tintseq_from_base_tstzspan(int arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tintseq_from_base_tstzspan requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tintseq_from_base_tstzspan(arg0, arg1); + } + + /** MEOS {@code tintseqset_from_base_tstzspanset} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tintseqset_from_base_tstzspanset(int arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tintseqset_from_base_tstzspanset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tintseqset_from_base_tstzspanset(arg0, arg1); + } + + /** MEOS {@code tnot_tbool} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer tnot_tbool(jnr.ffi.Pointer arg0) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tnot_tbool requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tnot_tbool(arg0); + } + + /** MEOS {@code tor_bool_tbool} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer tor_bool_tbool(int arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tor_bool_tbool requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tor_bool_tbool(arg0, arg1); + } + + /** MEOS {@code tor_tbool_bool} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer tor_tbool_bool(jnr.ffi.Pointer arg0, int arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tor_tbool_bool requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tor_tbool_bool(arg0, arg1); + } + + /** MEOS {@code tor_tbool_tbool} — meos.h · scalar / stateless. */ + public static jnr.ffi.Pointer tor_tbool_tbool(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tor_tbool_tbool requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tor_tbool_tbool(arg0, arg1); + } + + /** MEOS {@code tpointseq_from_base_tstzset} — meos_geo.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tpointseq_from_base_tstzset(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tpointseq_from_base_tstzset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tpointseq_from_base_tstzset(arg0, arg1); + } + + /** MEOS {@code tpointseq_from_base_tstzspan} — meos_geo.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tpointseq_from_base_tstzspan(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1, int arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tpointseq_from_base_tstzspan requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tpointseq_from_base_tstzspan(arg0, arg1, arg2); + } + + /** MEOS {@code tpointseq_make_coords} — meos_geo.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tpointseq_make_coords(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1, jnr.ffi.Pointer arg2, jnr.ffi.Pointer arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tpointseq_make_coords requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tpointseq_make_coords(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); + } + + /** MEOS {@code tpointseqset_from_base_tstzspanset} — meos_geo.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tpointseqset_from_base_tstzspanset(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1, int arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tpointseqset_from_base_tstzspanset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tpointseqset_from_base_tstzspanset(arg0, arg1, arg2); + } + + /** MEOS {@code tsequence_make} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tsequence_make(jnr.ffi.Pointer arg0, int arg1, int arg2, int arg3, int arg4, int arg5) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tsequence_make requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tsequence_make(arg0, arg1, arg2, arg3, arg4, arg5); + } + + /** MEOS {@code tsequenceset_make} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer tsequenceset_make(jnr.ffi.Pointer arg0, int arg1, int arg2) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("tsequenceset_make requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.tsequenceset_make(arg0, arg1, arg2); + } + + /** MEOS {@code ttextseq_from_base_tstzset} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer ttextseq_from_base_tstzset(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("ttextseq_from_base_tstzset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.ttextseq_from_base_tstzset(arg0, arg1); + } + + /** MEOS {@code ttextseq_from_base_tstzspan} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer ttextseq_from_base_tstzspan(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("ttextseq_from_base_tstzspan requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.ttextseq_from_base_tstzspan(arg0, arg1); + } + + /** MEOS {@code ttextseqset_from_base_tstzspanset} — meos.h · whole-sequence constructor — not a per-event op. */ + public static jnr.ffi.Pointer ttextseqset_from_base_tstzspanset(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("ttextseqset_from_base_tstzspanset requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.ttextseqset_from_base_tstzspanset(arg0, arg1); + } + + /** MEOS {@code union_cbuffer_set} — meos_cbuffer.h · scalar / stateless. */ + public static jnr.ffi.Pointer union_cbuffer_set(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("union_cbuffer_set requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.union_cbuffer_set(arg0, arg1); + } + + /** MEOS {@code union_npoint_set} — meos_npoint.h · scalar / stateless. */ + public static jnr.ffi.Pointer union_npoint_set(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("union_npoint_set requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.union_npoint_set(arg0, arg1); + } + + /** MEOS {@code union_pose_set} — meos_pose.h · scalar / stateless. */ + public static jnr.ffi.Pointer union_pose_set(jnr.ffi.Pointer arg0, jnr.ffi.Pointer arg1) { + if (!MeosOpsRuntime.MEOS_AVAILABLE) + throw new UnsupportedOperationException("union_pose_set requires libmeos — set -Dmobilityflink.meos.enabled=true"); + return functions.GeneratedFunctions.union_pose_set(arg0, arg1); + } + +} diff --git a/flink-processor/src/test/java/org/mobilitydb/flink/meos/MeosFacadeSmokeTest.java b/flink-processor/src/test/java/org/mobilitydb/flink/meos/MeosFacadeSmokeTest.java new file mode 100644 index 0000000..93165d7 --- /dev/null +++ b/flink-processor/src/test/java/org/mobilitydb/flink/meos/MeosFacadeSmokeTest.java @@ -0,0 +1,79 @@ +package org.mobilitydb.flink.meos; + +import functions.GeneratedFunctions; +import jnr.ffi.Pointer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Per-family runtime check that the generated MEOS facade calls into libmeos and + * returns correct results. Each family constructs a value through a {@code MeosOps*} + * facade method and reads it back. Runs only with {@code -Dmobilityflink.meos.enabled=true} + * and a libmeos on the load path; the extended families (cbuffer, npoint, pose) require a + * libmeos built with {@code -DCBUFFER=ON -DNPOINT=ON -DPOSE=ON -DRGEO=ON}. + */ +@EnabledIfSystemProperty(named = "mobilityflink.meos.enabled", matches = "true") +class MeosFacadeSmokeTest { + + @BeforeAll + static void init() { + // No-op error handler so a parse error returns rather than terminating the JVM. + GeneratedFunctions.meos_initialize_error_handler((level, code, message) -> { }); + GeneratedFunctions.meos_initialize(); + } + + @AfterAll + static void finalizeMeos() { + GeneratedFunctions.meos_finalize(); + } + + @Test + void coreTbox() { + Pointer tbox = MeosOpsTBox.tbox_in("TBOX X([1, 2])"); + assertNotNull(tbox); + assertTrue(MeosOpsTBox.tbox_out(tbox, 6).contains("TBOX")); + } + + @Test + void geoStbox() { + Pointer stbox = MeosOpsSTBox.stbox_in("STBOX X((1,1),(2,2))"); + assertNotNull(stbox); + assertTrue(MeosOpsSTBox.stbox_out(stbox, 6).contains("STBOX")); + } + + @Test + void geoGeometry() { + Pointer geom = MeosOpsFreeGeo.geom_in("POINT(1 1)", 0); + assertNotNull(geom); + assertTrue(MeosOpsFreeGeo.geo_as_text(geom, 6).toUpperCase().contains("POINT")); + } + + @Test + void cbuffer() { + Pointer cb = MeosOpsFreeCbuffer.cbuffer_make(MeosOpsFreeGeo.geom_in("POINT(1 1)", 0), 0.5); + assertNotNull(cb); + assertEquals(0.5, MeosOpsFreeCbuffer.cbuffer_radius(cb), 1e-9); + assertNotNull(MeosOpsFreeCbuffer.cbuffer_out(cb, 6)); + } + + @Test + void npoint() { + Pointer np = MeosOpsFreeNpoint.npoint_make(1, 0.5); + assertNotNull(np); + assertEquals(1, MeosOpsFreeNpoint.npoint_route(np)); + assertEquals(0.5, MeosOpsFreeNpoint.npoint_position(np), 1e-9); + } + + @Test + void pose() { + Pointer pose = MeosOpsFreePose.pose_in("Pose(Point(1 1), 0.5)"); + assertNotNull(pose); + assertNotNull(MeosOpsFreePose.pose_out(pose, 6)); + } +} diff --git a/flink-processor/tools/parity/emit_gap_methods.py b/flink-processor/tools/parity/emit_gap_methods.py new file mode 100644 index 0000000..ef895a5 --- /dev/null +++ b/flink-processor/tools/parity/emit_gap_methods.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""Emit forwarding facade methods for MEOS public-surface functions that the +generated MeosOps* facade does not yet expose. + +Each method forwards to its `functions.GeneratedFunctions` export using the +exact JMEOS signature (captured via `javap`), so the output compiles by +construction. Family and nature (scalar / sequence-constructor / tiling) are +recorded in the Javadoc; the wiring tier is governed by the wirings layer, not +by the presence of the forwarder. + +Run from flink-processor/: + javap -classpath jar/JMEOS.jar -p functions.GeneratedFunctions > /tmp/gen_sigs.txt + python3 tools/parity/emit_gap_methods.py /tmp/gen_sigs.txt +""" +import re, os, sys, glob + +HERE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +INC = "/home/esteban/src/MobilityDB/meos/include" +PUBLIC_HEADERS = ["meos.h", "meos_geo.h", "meos_cbuffer.h", "meos_npoint.h", "meos_pose.h", "meos_rgeo.h"] +FACADE = os.path.join(HERE, "src/main/java/org/mobilitydb/flink/meos") +OUT = os.path.join(FACADE, "MeosOpsParityGaps.java") + +_DECL = re.compile(r'^\s*extern\s+.+?\b([a-z][A-Za-z0-9_]*)\s*\(', re.M) +_PUBSTATIC = re.compile(r'public static [A-Za-z0-9_.<>\[\]]+ ([a-z0-9_]+)\(') +_SIG = re.compile(r'public static (\S+) (\w+)\(([^)]*)\);') + + +def family(name, fam): + return fam.get(name, "meos.h") + + +def nature(name): + if name.endswith("_make") or "_from_base_" in name or name.endswith("make_coords"): + return "whole-sequence constructor — not a per-event op" + if name.endswith("_tiles"): + return "multidimensional tiling (windowed)" + return "scalar / stateless" + + +def main(): + sigfile = sys.argv[1] if len(sys.argv) > 1 else "/tmp/parity/gen_sigs.txt" + fam, pub = {}, set() + for h in PUBLIC_HEADERS: + for n in set(_DECL.findall(open(os.path.join(INC, h)).read())): + pub.add(n); fam.setdefault(n, h) + facade = set() + for f in glob.glob(os.path.join(FACADE, "MeosOps*.java")): + facade |= set(_PUBSTATIC.findall(open(f).read())) + # all JMEOS signatures, grouped by name + sigs = {} + for line in open(sigfile): + m = _SIG.search(line) + if m: + ret, name, args = m.group(1), m.group(2), m.group(3).strip() + sigs.setdefault(name, []).append((ret, args)) + missing = sorted(n for n in (pub & set(sigs)) - facade) + + L = ["package org.mobilitydb.flink.meos;", "", + "/**", " * Forwarding facade methods for MEOS public-surface functions not emitted", + " * by the tier-aware code generator. Each method delegates to its JMEOS", + " * {@code functions.GeneratedFunctions} export under the shared", + " * {@link MeosOpsRuntime#MEOS_AVAILABLE} guard.", + " */", + "public final class MeosOpsParityGaps {", "", + " private MeosOpsParityGaps() { /* utility */ }", ""] + count = 0 + for name in missing: + for ret, args in sigs[name]: + params = [a.strip() for a in args.split(",")] if args else [] + decl = ", ".join(f"{t} arg{i}" for i, t in enumerate(params)) + call = ", ".join(f"arg{i}" for i in range(len(params))) + ret_kw = "" if ret == "void" else "return " + L += [f" /** MEOS {{@code {name}}} — {family(name, fam)} · {nature(name)}. */", + f" public static {ret} {name}({decl}) {{", + f" if (!MeosOpsRuntime.MEOS_AVAILABLE)", + f' throw new UnsupportedOperationException("{name} requires libmeos' + f' — set -Dmobilityflink.meos.enabled=true");', + f" {ret_kw}functions.GeneratedFunctions.{name}({call});", + " }", ""] + count += 1 + L.append("}") + open(OUT, "w").write("\n".join(L) + "\n") + print(f"missing public-surface functions: {len(missing)}") + print(f"forwarding methods emitted: {count}") + print(f"wrote {OUT}") + + +if __name__ == "__main__": + main() diff --git a/flink-processor/tools/parity/parity_audit.py b/flink-processor/tools/parity/parity_audit.py new file mode 100644 index 0000000..7548738 --- /dev/null +++ b/flink-processor/tools/parity/parity_audit.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +"""Symbol-level parity audit for the MobilityFlink generated MEOS facade. + +Measures, per type family, how much of the MEOS public-header function surface +the generated facade (org.mobilitydb.flink.meos.MeosOps*) exposes, cross-checked +against the JMEOS jar export set so a method only counts when it is genuinely +bindable. + +Reference surface = the MEOS public C API (meos/include/meos*.h, excluding the +meos_internal*.h headers). This is the surface JMEOS mirrors and the facade +delegates to, so it is the exact denominator for a Flink binding. A second view +maps the facade against the MobilityDB SQL layer's underlying C symbols for +comparability with the SQL-surface bindings. + +Coverage reported here is STATIC: the facade method exists and binds a JMEOS +symbol of matching arity. Runtime behaviour is exercised separately by the +per-family smoke tests. + +Usage: + python3 tools/parity/parity_audit.py \ + --meos-include /path/to/MobilityDB/meos/include \ + --jar jar/JMEOS.jar \ + --facade src/main/java/org/mobilitydb/flink/meos \ + --mdb-sql /path/to/MobilityDB/mobilitydb/sql \ + --out docs/parity-status.md +""" +import argparse, re, os, glob, subprocess, collections +from datetime import date + +PUBLIC_HEADERS = ["meos.h", "meos_geo.h", "meos_cbuffer.h", + "meos_npoint.h", "meos_pose.h", "meos_rgeo.h"] +FAMILY = {"meos.h": "core temporal / set / span / spanset / tbox", + "meos_geo.h": "geo (tgeo / tpoint / stbox)", + "meos_cbuffer.h": "cbuffer", "meos_npoint.h": "npoint", + "meos_pose.h": "pose", "meos_rgeo.h": "rgeo"} + +_DECL = re.compile(r'^\s*extern\s+.+?\b([a-z][A-Za-z0-9_]*)\s*\(', re.M) +_PUBSTATIC = re.compile(r'public static [A-Za-z0-9_.<>\[\]]+ ([a-z0-9_]+)\(') + +# MobilityDB SQL out-of-scope bucketing (PG-only; no MEOS/Flink equivalent). +OOS_SECTIONS = { + "temporal/011_span_indexes.in.sql", "temporal/012_spanset_indexes.in.sql", + "temporal/013_set_indexes.in.sql", "temporal/019_geo_constructors.in.sql", + "temporal/043_temporal_gist.in.sql", "temporal/044_temporal_spgist.in.sql", + "temporal/999_oid_cache.in.sql", "geo/073_tgeo_gist.in.sql", + "geo/073_tpoint_gist.in.sql", "geo/074_tgeo_spgist.in.sql", + "geo/074_tpoint_spgist.in.sql", "cbuffer/166_tcbuffer_indexes.in.sql", + "npoint/092_tnpoint_gin.in.sql", "npoint/098_tnpoint_indexes.in.sql", + "pose/114_tpose_indexes.in.sql", "rgeo/134_trgeo_indexes.in.sql", +} +OOS_SUFFIXES = ("_in", "_out", "_recv", "_send", "_typmod_in", "_typmod_out", + "_transfn", "_combinefn", "_finalfn", "_serialize", "_deserialize", + "_sel", "_joinsel", "_supportfn", "_analyze", + "_cmp", "_eq", "_ne", "_lt", "_le", "_gt", "_ge", "_hash", "_hash_extended") +OOS_NAMES = {"range", "multirange", "create_trip", "transform_gk"} + + +def jmeos_symbols(jar): + out = subprocess.run(["javap", "-classpath", jar, "-p", + "functions.GeneratedFunctions"], + capture_output=True, text=True).stdout + return set(re.findall(r'\b([a-z][a-z0-9_]+)\(', out)) + + +def facade_methods(d): + syms = set() + for f in glob.glob(os.path.join(d, "MeosOps*.java")): + syms |= set(_PUBSTATIC.findall(open(f).read())) + return syms + + +def public_surface(inc): + fam, allpub = {}, set() + for h in PUBLIC_HEADERS: + names = set(_DECL.findall(open(os.path.join(inc, h)).read())) + for n in names: + allpub.add(n); fam.setdefault(n, h) + return allpub, fam + + +def sql_csymbols(root): + create = re.compile(r'CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+([A-Za-z0-9_]+)\s*\(', re.I) + csym = re.compile(r"AS\s+'[^']*'\s*,\s*'([A-Za-z0-9_]+)'", re.I) + langsql = re.compile(r"LANGUAGE\s+'?(?:sql|plpgsql)'?", re.I) + addressable, oos, sqlc = set(), 0, 0 + for path in glob.glob(os.path.join(root, "**", "*.in.sql"), recursive=True): + section = os.path.relpath(path, root).replace(os.sep, "/") + txt = open(path, encoding="utf-8", errors="replace").read() + for m in create.finditer(txt): + name = m.group(1); tail = txt[m.end():m.end() + 1200].split(';')[0] + cm = csym.search(tail); sym = cm.group(1) if cm else None + key = (sym or name).lower() + if section in OOS_SECTIONS or key in OOS_NAMES or key.endswith(OOS_SUFFIXES): + oos += 1; continue + if sym is None: + sqlc += 1; continue + addressable.add(sym.lower()) + return addressable, oos, sqlc + + +def main(): + ap = argparse.ArgumentParser() + here = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + ap.add_argument("--meos-include", default="/home/esteban/src/MobilityDB/meos/include") + ap.add_argument("--jar", default=os.path.join(here, "jar", "JMEOS.jar")) + ap.add_argument("--facade", default=os.path.join(here, "src/main/java/org/mobilitydb/flink/meos")) + ap.add_argument("--mdb-sql", default="/home/esteban/src/MobilityDB/mobilitydb/sql") + ap.add_argument("--out", default=os.path.join(here, "docs", "parity-status.md")) + a = ap.parse_args() + + jm = jmeos_symbols(a.jar) + fa = facade_methods(a.facade) & jm + pub, fam = public_surface(a.meos_include) + bindable = pub & jm + covered = bindable & fa + missing = sorted(bindable - fa) + + bc, cc = collections.Counter(), collections.Counter() + for n in bindable: + bc[fam[n]] += 1 + if n in covered: + cc[fam[n]] += 1 + + addr, oos, sqlc = sql_csymbols(a.mdb_sql) + sql_cov = len(addr & fa); sql_bindable = len(addr & jm) + + pct = lambda c, t: (100.0 * c / t) if t else 0.0 + L = [] + L.append("# MobilityFlink parity status — MEOS surface audit\n") + L.append(f"Generated {date.today().isoformat()} by `tools/parity/parity_audit.py`.\n") + L.append("The MobilityFlink MEOS facade (`org.mobilitydb.flink.meos.MeosOps*`) exposes " + "MEOS C functions to Flink through JMEOS. This audit measures, per type family, " + "the share of the **MEOS public C API** that the facade exposes and that JMEOS binds.\n") + L.append(f"**Headline.** The facade exposes **{len(covered)} of {len(bindable)} " + f"public MEOS functions ({pct(len(covered), len(bindable)):.1f}%)**. " + f"The MEOS public surface (`meos/include/meos*.h`, excluding internal headers) is " + f"{len(pub)} functions; JMEOS binds {len(bindable)} of them. " + f"{len(missing)} bindable functions are not exposed (listed in §3).\n") + L.append("Coverage is **static**: a function counts as covered when the facade declares a " + "method of the same name and arity that delegates to a JMEOS export.\n") + L.append("Per-family runtime behaviour is asserted by " + "`src/test/java/org/mobilitydb/flink/meos/MeosFacadeSmokeTest.java`, which constructs " + "and reads back a value in the core, geo, cbuffer, npoint and pose families through the " + "facade against libmeos. The cbuffer, npoint and pose families require a libmeos built " + "with the extended modules (`-DCBUFFER=ON -DNPOINT=ON -DPOSE=ON -DRGEO=ON`); the stock " + "library carries the core and geo surfaces only.\n") + L.append("## 1. Reference surface and method\n") + L.append("- **Denominator**: distinct function names declared `extern` in the MEOS public " + "headers `meos.h`, `meos_geo.h`, `meos_cbuffer.h`, `meos_npoint.h`, `meos_pose.h`, " + "`meos_rgeo.h`. Internal headers (`meos_internal*.h`) are excluded.\n") + L.append("- **Numerator**: `public static` methods on the generated `MeosOps*` facade whose " + "name is also a `functions.GeneratedFunctions` export in the bundled JMEOS jar.\n") + L.append(f"- **JMEOS jar**: {os.path.relpath(a.jar, here)} exports {len(jm)} static methods.\n") + L.append("## 2. Per-family coverage of the public MEOS surface\n") + L.append("| Family (header) | Public ∩ JMEOS | Exposed | Missing | Coverage |") + L.append("|---|---:|---:|---:|---:|") + for h in PUBLIC_HEADERS: + b, c = bc[h], cc[h] + L.append(f"| {FAMILY[h]} (`{h}`) | {b} | {c} | {b - c} | {pct(c, b):.1f}% |") + L.append(f"| **total** | **{len(bindable)}** | **{len(covered)}** | " + f"**{len(bindable) - len(covered)}** | **{pct(len(covered), len(bindable)):.1f}%** |\n") + L.append("## 3. Bindable MEOS functions not exposed by the facade\n") + L.append(f"{len(missing)} functions are present in the public MEOS headers and bound by " + "JMEOS but not generated into the facade:\n") + for h in PUBLIC_HEADERS: + ms = [n for n in missing if fam[n] == h] + if ms: + L.append(f"- **`{h}`** ({len(ms)}): " + ", ".join(f"`{n}`" for n in ms)) + L.append("") + L.append("## 4. MobilityDB SQL-surface cross-check\n") + L.append("For comparability with the SQL-surface bindings, the facade is also matched " + "against the underlying MEOS C symbol of each addressable `CREATE FUNCTION` in " + "`mobilitydb/sql/**/*.in.sql` (PG-only sections and helper symbols bucketed out; " + f"{oos} out-of-scope, {sqlc} SQL/plpgsql-composed functions with no single C symbol). " + "The extension wrapper occasionally renames the MEOS function it calls, so this view " + "is a lower bound.\n") + L.append(f"- Addressable distinct C symbols: **{len(addr)}**; bound by JMEOS: **{sql_bindable}**; " + f"exposed by the facade: **{sql_cov}** " + f"({pct(sql_cov, sql_bindable):.1f}% of the JMEOS-bindable SQL surface).\n") + + md = "\n".join(L) + "\n" + os.makedirs(os.path.dirname(a.out), exist_ok=True) + open(a.out, "w").write(md) + + print(f"public MEOS surface: {len(pub)}") + print(f" bound by JMEOS: {len(bindable)}") + print(f" exposed by facade: {len(covered)} ({pct(len(covered), len(bindable)):.1f}%)") + print(f" missing (bindable): {len(missing)}") + print(f"SQL addressable C-symbols: {len(addr)} (bindable {sql_bindable}, exposed {sql_cov})") + print(f"wrote {a.out}") + + +if __name__ == "__main__": + main()