From 577284e4703924fb2f84aada06e5ea04ed6505b0 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sat, 15 Aug 2026 17:37:20 +0200 Subject: [PATCH] Compile the module against the installed MEOS umbrella header The module compiled against MobilityDB's source tree, where meos.h is not the header MEOS publishes. MEOS assembles the installed : it splices its PostgreSQL-compat definitions into it under #ifndef POSTGRES_H, which supplies Datum, DateADT, TimestampTz, Interval and the de-prefixed base I/O -- interval_in, date_in, timestamptz_in and their siblings, named without a meos_ prefix so they do not collide with a real PostgreSQL. None of that is in the source copy, so calls to those functions were implicitly declared and C99 rejects them. The build installs MEOS and compiles against that prefix. The preamble stops including the vendored , which defined POSTGRES_H first and made skip the whole spliced block, and takes directly rather than through it. The Datum accessors the spliced block leaves out are defined here, keyed on the pointer width exactly as PostgreSQL keys USE_FLOAT8_BYVAL on SIZEOF_VOID_P >= 8. With -sMEMORY64=1 a Datum is 8 bytes, so a TimestampTz or an int64 span bound rides inside it rather than behind a pointer; reading one through a pointer returns whatever the value addresses, which makes a bigint span report a neighbour it does not touch as adjacent. Both widths stay correct. --- Dockerfile | 9 +++- codegen/res/bindings_c_header.c.template | 67 +++++++++++++++++++++--- core/c-src/bindings.c | 67 +++++++++++++++++++++--- 3 files changed, 127 insertions(+), 16 deletions(-) diff --git a/Dockerfile b/Dockerfile index dc03c83..67b82fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -220,7 +220,12 @@ RUN PG_CONFIG_H=/root/MobilityDB/build/pgtypes/pg_config.h \ "$PG_CONFIG_H" # COMPILING: libmeos.a -RUN cmake --build /root/MobilityDB/build --target meos --parallel "$(nproc)" +# The install step produces the header this module compiles against. MEOS +# assembles the installed rather than shipping the source one: it +# splices the PostgreSQL-compat definitions and the de-prefixed base I/O into +# it, so interval_in, date_in and their siblings are declared only there. +RUN cmake --build /root/MobilityDB/build --target meos --parallel "$(nproc)" \ + && cmake --install /root/MobilityDB/build --prefix /root/meos-install # EMCC: link everything into meos.js + meos.wasm # @@ -264,7 +269,7 @@ RUN mkdir -p /app/wasm \ /root/gsl-wasm/lib/libgslcblas.a \ /root/json-c-install/lib/libjson-c.a \ /root/h3-install/lib/libh3.a \ - -I/root/MobilityDB/meos/include \ + -I/root/meos-install/include \ -I/root/geos/include \ -I/root/geos/build/capi \ -I/root/MobilityDB/pgtypes \ diff --git a/codegen/res/bindings_c_header.c.template b/codegen/res/bindings_c_header.c.template index 815c573..ecd4463 100644 --- a/codegen/res/bindings_c_header.c.template +++ b/codegen/res/bindings_c_header.c.template @@ -1,7 +1,21 @@ #include -#include -#include +#include +/* + * stands in for here, and including both breaks it. + * + * The installed is assembled rather than copied from the source tree: + * MEOS splices its PostgreSQL-compat definitions into it under `#ifndef + * POSTGRES_H`, which supplies Datum, DateADT, TimestampTz, Interval and the + * de-prefixed base I/O (interval_in, date_in, timestamptz_in, …) that carry no + * meos_ prefix precisely so they do not collide with a real PostgreSQL. Pulling + * in the vendored first defines POSTGRES_H, the spliced block is + * skipped, and those types and declarations vanish. + * + * So the module compiles against the installed umbrella alone. Only the Datum + * accessors are missing from it, and they are defined below beside the + * Int64GetDatum/Float8GetDatum this file already supplies for the same reason. + */ #include #include #include @@ -14,23 +28,62 @@ #include /* - * Implementations of Int64GetDatum and Float8GetDatum required when - * USE_FLOAT8_BYVAL is disabled (by-reference mode for 64-bit types). + * The Datum accessors PostgreSQL declares in postgres.h. + * + * Whether a 64-bit value rides inside a Datum or behind a pointer is decided by + * the pointer width: PostgreSQL sets USE_FLOAT8_BYVAL when SIZEOF_VOID_P >= 8, + * and Datum is uintptr_t. This module builds with -sMEMORY64=1, so pointers are + * 8 bytes and a TimestampTz or an int64 span bound rides inside the Datum + * itself. Reading such a Datum through a pointer returns whatever the value + * happens to address, which is how a bigint span reports a neighbour it does + * not touch as adjacent. + * + * The condition below reproduces PostgreSQL's, so both widths stay correct. */ -#ifndef USE_FLOAT8_BYVAL -Datum Int64GetDatum(int64 X) { +#include + +typedef double float8; + +#if UINTPTR_MAX > 0xFFFFFFFFu +#define USE_FLOAT8_BYVAL 1 +#endif + +#ifndef PointerGetDatum +#define PointerGetDatum(X) ((Datum) (X)) +#endif + +#ifdef USE_FLOAT8_BYVAL + +#define DatumGetInt64(X) ((int64) (X)) +#define Int64GetDatum(X) ((Datum) (X)) + +static inline Datum Float8GetDatum(float8 X) { + union { float8 value; int64 retval; } myunion; + myunion.value = X; + return (Datum) myunion.retval; +} + +#else + +#define DatumGetInt64(X) (*((int64 *) DatumGetPointer(X))) + +static inline Datum Int64GetDatum(int64 X) { int64 *ptr = (int64 *) malloc(sizeof(int64)); *ptr = X; return PointerGetDatum(ptr); } -Datum Float8GetDatum(float8 X) { +static inline Datum Float8GetDatum(float8 X) { float8 *ptr = (float8 *) malloc(sizeof(float8)); *ptr = X; return PointerGetDatum(ptr); } + #endif +#define DatumGetTimestampTz(X) ((TimestampTz) DatumGetInt64(X)) +#define TimestampTzGetDatum(X) Int64GetDatum(X) + /* --- Error handler --- */ /* * A static C error handler stores the last errlevel/errcode/errmsg produced diff --git a/core/c-src/bindings.c b/core/c-src/bindings.c index f14637c..e1d0fb2 100644 --- a/core/c-src/bindings.c +++ b/core/c-src/bindings.c @@ -1,8 +1,22 @@ /* AUTO-GENERATED - DO NOT EDIT. Run: npm run generate */ #include -#include -#include +#include +/* + * stands in for here, and including both breaks it. + * + * The installed is assembled rather than copied from the source tree: + * MEOS splices its PostgreSQL-compat definitions into it under `#ifndef + * POSTGRES_H`, which supplies Datum, DateADT, TimestampTz, Interval and the + * de-prefixed base I/O (interval_in, date_in, timestamptz_in, …) that carry no + * meos_ prefix precisely so they do not collide with a real PostgreSQL. Pulling + * in the vendored first defines POSTGRES_H, the spliced block is + * skipped, and those types and declarations vanish. + * + * So the module compiles against the installed umbrella alone. Only the Datum + * accessors are missing from it, and they are defined below beside the + * Int64GetDatum/Float8GetDatum this file already supplies for the same reason. + */ #include #include #include @@ -15,23 +29,62 @@ #include /* - * Implementations of Int64GetDatum and Float8GetDatum required when - * USE_FLOAT8_BYVAL is disabled (by-reference mode for 64-bit types). + * The Datum accessors PostgreSQL declares in postgres.h. + * + * Whether a 64-bit value rides inside a Datum or behind a pointer is decided by + * the pointer width: PostgreSQL sets USE_FLOAT8_BYVAL when SIZEOF_VOID_P >= 8, + * and Datum is uintptr_t. This module builds with -sMEMORY64=1, so pointers are + * 8 bytes and a TimestampTz or an int64 span bound rides inside the Datum + * itself. Reading such a Datum through a pointer returns whatever the value + * happens to address, which is how a bigint span reports a neighbour it does + * not touch as adjacent. + * + * The condition below reproduces PostgreSQL's, so both widths stay correct. */ -#ifndef USE_FLOAT8_BYVAL -Datum Int64GetDatum(int64 X) { +#include + +typedef double float8; + +#if UINTPTR_MAX > 0xFFFFFFFFu +#define USE_FLOAT8_BYVAL 1 +#endif + +#ifndef PointerGetDatum +#define PointerGetDatum(X) ((Datum) (X)) +#endif + +#ifdef USE_FLOAT8_BYVAL + +#define DatumGetInt64(X) ((int64) (X)) +#define Int64GetDatum(X) ((Datum) (X)) + +static inline Datum Float8GetDatum(float8 X) { + union { float8 value; int64 retval; } myunion; + myunion.value = X; + return (Datum) myunion.retval; +} + +#else + +#define DatumGetInt64(X) (*((int64 *) DatumGetPointer(X))) + +static inline Datum Int64GetDatum(int64 X) { int64 *ptr = (int64 *) malloc(sizeof(int64)); *ptr = X; return PointerGetDatum(ptr); } -Datum Float8GetDatum(float8 X) { +static inline Datum Float8GetDatum(float8 X) { float8 *ptr = (float8 *) malloc(sizeof(float8)); *ptr = X; return PointerGetDatum(ptr); } + #endif +#define DatumGetTimestampTz(X) ((TimestampTz) DatumGetInt64(X)) +#define TimestampTzGetDatum(X) Int64GetDatum(X) + /* --- Error handler --- */ /* * A static C error handler stores the last errlevel/errcode/errmsg produced