From de7297316fe027d9fe6003a789991f0f3488348d Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 16 Jul 2026 23:57:44 +0200 Subject: [PATCH] Recover the bare uint32 typedef in the catalog The parser's type-recovery table restored the bare PostgreSQL uint64 typedef (hash seeds, cell ids) but not uint32, so every MEOS *_hash return -- declared uint32 in the headers -- stayed collapsed to a signed int. Hash values >= 2**31 then flipped sign in every generated binding (e.g. MobilityDuck registered span_hash as INTEGER instead of UINTEGER). Add the uint32 -> uint32_t entry, mirroring uint64 -> uint64_t, plus a regression test asserting the *_hash returns recover, so this sibling of the recurrently-dropped uint64 entry cannot be lost in a refactor. --- parser/typerecover.py | 1 + tests/test_typerecover.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/parser/typerecover.py b/parser/typerecover.py index ded1806..59dd454 100644 --- a/parser/typerecover.py +++ b/parser/typerecover.py @@ -32,6 +32,7 @@ _TYPE_MAP = { "bool": "bool", "int64": "int64_t", + "uint32": "uint32_t", "uint64": "uint64_t", "Timestamp": "Timestamp", "TimestampTz": "TimestampTz", diff --git a/tests/test_typerecover.py b/tests/test_typerecover.py index 00a0f0b..c0374c9 100644 --- a/tests/test_typerecover.py +++ b/tests/test_typerecover.py @@ -133,6 +133,21 @@ def test_uint64_recovered(self): self.assertEqual(self._ret("span_hash_extended"), "uint64_t") self.assertIn("uint64_t", self._param_ctypes("set_hash_extended")) + def test_uint32_recovered(self): + # The bare PG ``uint32`` typedef collapses to ``int`` and must recover + # to ``uint32_t`` (like ``uint64`` -> ``uint64_t``), else the unsigned + # 32-bit hash return renders as a signed ``int`` in every generated + # binding (values >= 2**31 flip sign). Every MEOS ``*_hash`` returns the + # bare ``uint32`` typedef, so they recover only when the ``uint32`` map + # entry is present — a guard against dropping it, the sibling of the + # recurrently-lost ``uint64`` entry above. + for name in ("set_hash", "span_hash", "spanset_hash", + "tbox_hash", "temporal_hash"): + self.assertEqual(self._ret(name), "uint32_t", name) + # Genuine ``int`` returns (e.g. numValues) must stay untouched: the + # recovery only fires where the header text spells ``uint32``. + self.assertEqual(self._ret("set_num_values"), "int") + def test_cell_id_canonical_normalized_uniform(self): # H3Index (libh3's typedef, whose fully-resolved canonical is the platform # "unsigned long") and Quadbin (MobilityDB's typedef, recovered to "uint64_t")