feat(bindings): tgeometry + tgeography + tgeogpoint parity — cumulative (consolidates #90, #91)#92
Closed
estebanzimanyi wants to merge 18 commits intomainfrom
Closed
feat(bindings): tgeometry + tgeography + tgeogpoint parity — cumulative (consolidates #90, #91)#92estebanzimanyi wants to merge 18 commits intomainfrom
estebanzimanyi wants to merge 18 commits intomainfrom
Conversation
…ueTimeTiles)
Adds the single-bin getter family and the LIST<TBOX> emitters that are
the scalar counterparts of the prior bins() / tboxes() table functions.
getBin(integer, integer, integer) -> intspan
getBin(bigint, bigint, bigint) -> bigintspan
getBin(double, double, double) -> floatspan
getBin(date, interval, date) -> datespan
getBin(timestamptz, interval, timestamptz) -> tstzspan
valueTiles(tbox, vsize [, vorigin]) -> LIST<tbox>
timeTiles(tbox, duration [, torigin]) -> LIST<tbox>
valueTimeTiles(tbox, vsize, duration
[, vorigin, torigin]) -> LIST<tbox>
The tbox emitters dispatch on tbox->span.spantype so the same SQL
function transparently handles TBOXINT and TBOXFLOAT inputs without
the user having to pick a numeric variant. Defaults match MobilityDB's
SQL: vorigin = 0, torigin = '2000-01-03'.
Test 025_temporal_tile_getters.test exercises all five getBin variants
and the three emitters across both int and float tboxes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous PR landed extent() for the 5 span types only. This expands
the same aggregate to cover the full MobilityDB SQL surface for fixed-
size-state extent variants (still avoiding the SkipList-state aggregates
like tcount / tmin / tcentroid / merge / appendInstant — those need a
larger lift).
Added (15 new overloads on the same `extent` aggregate set):
- extent(integer | bigint | double | date | timestamptz) -> typed span
- extent(intset | bigintset | floatset | dateset | tstzset) -> typed span
- extent(intspanset | bigintspanset | floatspanset
| datespanset | tstzspanset) -> typed span
- extent(tbox) -> tbox
- extent(stbox) -> stbox
- extent(tint | tfloat) -> tbox
- extent(tbool | ttext) -> tstzspan
- extent(tgeompoint) -> stbox
All variants share a small set of templated state types — Span, TBox,
STBox — and dispatch on the appropriate MEOS *_extent_transfn for the
input. Combine functions use span_extent_transfn / tbox_expand /
stbox_expand so parallel-agg merge works.
Tests in 030_aggregates_extent.test cover one assertion per category
plus NULL handling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, tsum, tavg, tcentroid)
Adds the harness for MEOS SkipList-state temporal aggregates and ships
seven of them. SkipList aggregates differ from the fixed-state extent()
family in two ways:
1. State holds a `SkipList *` pointer; the skiplist owns variable-size
heap-allocated Temporal* values, so the aggregate needs a destructor
(registered via UnaryAggregateDestructor with LEGACY destructor type).
2. Combine merges two skiplists via MEOS's `temporal_tagg_combinefn`,
which is exported from libmeos but not declared in the public
headers, so we forward-declare it here. Same goes for the per-
aggregate `datum_*` merge functors.
The MEOS combine semantics share Temporal* pointers between the two
input skiplists when both are non-empty: the returned skiplist holds
the merged elements while the "loser" skiplist still references the
same pointers. To avoid double-frees we walk the loser's elements and
NULL out keys/values before calling skiplist_free (which then only
releases the SkipList struct + freed[] array + elem array).
Implemented aggregates (single-input only — window aggregates and
extra base-type overloads are deferred):
tand(tbool) -> tbool
tor(tbool) -> tbool
tcount(tbool|tint|tfloat|ttext|tgeompoint) -> tint
tsum(tint|tfloat) -> same input type
tavg(tint|tfloat) -> tfloat
tcentroid(tgeompoint) -> tgeompoint
Deferred this PR:
- tmin / tmax — collide with the existing scalar Tmin(tbox|stbox) /
Tmax(tbox|stbox) under DuckDB's case-insensitive name resolution.
DuckDB rejects mixing scalar+aggregate overloads on the same
canonical name (raises "GetAlterInfo not implemented for this type").
Resolving needs either renaming the box scalars or aggregate
suffixing — out of scope for this PR.
- merge / appendInstant / appendSequence
- spanUnion / setUnion (need different state shape — span/set
skiplist instead of temporal)
- Window aggregates (wmin / wmax / wsum / wcount / wavg) — need an
extra interval argument
- tcentroid 3D dispatch — currently always uses datum_sum_double3 (2D)
- tcount over scalar timestamptz / tstzset / tstzspan / tstzspanset
inputs (use timestamptz_tcount_transfn etc.)
Test plan: 031_aggregates_skiplist.test (14 assertions) — one per
registered aggregate plus NULL/empty handling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…g/TmaxAgg Rename every registered SkipList aggregate to its Pascal-cased *Agg identifier per the proposal in MobilityDB issue #827 / PR #828: tand -> TandAgg tor -> TorAgg tcount -> TcountAgg tsum -> TsumAgg tavg -> TavgAgg tcentroid -> TcentroidAgg The new naming convention disambiguates SkipList aggregates from the same-stem scalar accessors (e.g. `Tmin(tbox)`) under DuckDB's case- folding catalog. With the rename in place the previously-blocked TminAgg / TmaxAgg now drop in cleanly, so this commit also adds them: TminAgg(tint | tfloat | ttext) -> same-typed temporal min TmaxAgg(tint | tfloat | ttext) -> same-typed temporal max The MobilityDB upstream PR #828 lands the matching aliases on the PG side; the original lowercase / camelCase names remain valid in PG for backward compat but collide in DuckDB and are intentionally not registered here. Test 031_aggregates_skiplist.test exercises all 11 aggregates (4 TcountAgg overloads + TandAgg + TorAgg + 3 TminAgg + 3 TmaxAgg + 2 TsumAgg + 2 TavgAgg + TcentroidAgg) plus NULL handling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tValueTimeTile + spatial
Adds the per-input lookup form of the tile family as a counterpart to
the LIST<TBOX>/LIST<STBOX> emitters previously shipped as
valueTiles / timeTiles / valueTimeTiles / spaceTiles / etc.
Temporal (3 names, 5 overloads):
getValueTile(double, double [, double=0.0]) -> tbox
getTBoxTimeTile(timestamptz, interval
[, timestamptz='2000-01-03']) -> tbox
getValueTimeTile(double, timestamptz, double, interval
[, double=0.0, timestamptz='2000-01-03']) -> tbox
Spatial (3 names, 6 overloads):
getSpaceTile(geom, xsize) -> stbox (uniform xyz)
getSpaceTile(geom, xsize, ysize, zsize [, sorigin]) -> stbox
getStboxTimeTile(timestamptz, interval
[, timestamptz='2000-01-03']) -> stbox
getSpaceTimeTile(geom, t, xsize, ysize, zsize, interval
[, sorigin, torigin]) -> stbox
All temporal variants funnel through the MEOS internal
`tbox_get_value_time_tile`. The basetype/spantype hint to MEOS comes
from the first argument: T_FLOAT8/T_FLOATSPAN for value-bearing
variants, T_TIMESTAMPTZ/T_TSTZSPAN for the time-only getter (this
detail is what mirrors how the MobilityDB PG layer derives the basetype
from the first SQL arg in `Tbox_get_value_time_tile_common`).
Spatial variants call `stbox_get_space_tile` / `stbox_get_time_tile` /
`stbox_get_space_time_tile` directly with a default-cached
`Point(0 0 0)` GSERIALIZED used when no explicit sorigin is supplied.
Test 026_single_tile_getters.test exercises every shape (10 assertions).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dow aggs / time-only TcountAgg / TcentroidAgg 3D dispatch
Completes the SkipList-aggregate surface in scope for this PR.
Added (24 new aggregate overloads on top of the previous 11):
MergeAgg(tbool|tint|tfloat|ttext|tgeompoint) -> same input
AppendInstantAgg(...) / AppendSequenceAgg(...) -> same input
(Temporal-state harness, distinct from the SkipList-state harness;
state holds a Temporal* directly and combine merges via temporal_merge.)
SpanUnionAgg(<typed-span> | <typed-spanset>) -> typed spanset
(SpanSet-state harness via span_union_transfn / spanset_union_transfn /
spanset_union_finalfn.)
SetUnionAgg(<scalar> | <typed-set>) -> typed set
(Set-state harness — value_union_transfn for scalars, set_union_transfn
for sets. Forward-declares value_union_transfn from meos_internal.h.)
WminAgg / WmaxAgg / WsumAgg / WavgAgg(tnumber, interval) -> same numeric type
WcountAgg(tnumber, interval) -> tint
(Binary-aggregate harness over (Temporal, Interval). WcountAgg uses
temporal_wagg_transform_transfn + temporal_transform_wcount,
forward-declared since they're not in installed public headers.)
TcountAgg(timestamptz | tstzset | tstzspan | tstzspanset) -> tint
(Distinct transfns: timestamptz_tcount_transfn / tstzset_*tcount_transfn /
tstzspan_tcount_transfn / tstzspanset_tcount_transfn.)
Fixed: TcentroidAgg's 3D dispatch was reading the wrong byte of
SkipList::extra (treated the LSB of `srid` as `hasz`). Now reads
GeoAggregateState{int32 srid; bool hasz;} via a mirror struct.
Test 031_aggregates_skiplist.test grew from 18 to 35 assertions covering
every shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… / modifiers / comparison Mirrors the temporal-generic portion of the tgeompoint surface for tgeometry. Most MEOS C functions reached here are subtype-agnostic (they take Temporal *), so the registrations reuse the same generic TemporalFunctions::* handlers that tgeompoint uses, with TGEOMPOINT() swapped for TGEOMETRY() and the value type swapped from POINT to GEOMETRY. Functions added (~50 overloads on top of the existing 22): - Accessors: valueSet, valueN, valueAtTimestamp, getTime, duration, lowerInc, upperInc, numInstants, instants, numSequences, sequences, startSequence, endSequence, sequenceN, numTimestamps, timestamps, startTimestamp, endTimestamp, timestampN, segments - Time-domain restrict: atTime / minusTime over (timestamptz, tstzset, tstzspan, tstzspanset); beforeTimestamp / afterTimestamp - Value restrict: atValues / minusValues over (geometry, geomset) - Spatial restrict: atGeometry, minusGeometry, minusStbox (reusing the tgeompoint-side Tgeo_* handlers — they already operate on Temporal *) - Modifiers: shiftTime, scaleTime, shiftScaleTime, appendInstant, appendSequence, insert, update, deleteTime over multiple time argument shapes - Comparison: temporal_eq / ne / lt / le / gt / ge / cmp + the matching =, <>, <, <=, >, >= operators Out of scope for this PR (planned follow-ups): - tgeometry comparison ops with geometry arg, distance / spatialrels / temporal-spatialrels (the bigger 060 / 064 / 070 / 072 SQL files) - tgeometry tile family + analytics + aggregates + GiST / SP-GiST - tgeography parity (entirely separate type registration) Test: 040_tgeometry_foundations.test — 19 assertions covering each category. Geometry values are emitted in EWKB-hex display rather than WKT, mirroring how tgeompoint test outputs are compared. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ps / spatial-rels / temporal-rels / distance)
Extends the foundational tgeometry surface from the previous commit
with the cross-type predicate family that mirrors what MobilityDB
ships in 060 / 062 / 070 / 072 / 064_tgeo_*.in.sql:
Box predicates (named function + operator):
temporal_overlaps / && over (tgeometry × stbox / tstzspan / tgeometry)
temporal_contains / @> over the same cross-product
temporal_contained / <@
temporal_same / ~=
temporal_adjacent / -|-
Position predicates (16 names — left / right / below / above / front /
back / before / after and their over-* variants) over (tgeometry ×
stbox / tgeometry); the four time-axis ones (before / after /
overbefore / overafter) also accept tstzspan.
Spatial relationships:
eContains / aContains (tgeometry, geometry, tgeometry)
eDisjoint / aDisjoint commutative (geo, tgeo) reuses (tgeo, geo)
eIntersects / aIntersects same
eTouches / aTouches same
eDwithin / aDwithin with float distance argument
Temporal spatial relationships (return tbool):
tContains, tDisjoint, tIntersects, tTouches over (geo, tgeo) /
(tgeo, geo) / (tgeo, tgeo); tDwithin with float distance.
Distance:
tdistance(tgeometry, geometry|tgeometry) -> tfloat
<-> operator equivalents.
The MEOS exports reached here all take Temporal * (subtype-agnostic),
so this file is purely registration glue — templated executors decode
the input blobs, call the MEOS function, and re-encode the result.
Test 040_tgeometry_parity.test grew from 19 to 33 assertions and was
renamed from 040_tgeometry_foundations.test to reflect the broader
scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…orm / coercions / centroid / convexHull / traversedArea Adds the spatial-functions surface from MobilityDB's 056_tgeo_spatialfuncs.in.sql for tgeometry: SRID(tgeometry) -> integer setSRID(tgeometry, integer) -> tgeometry transform(tgeometry, integer) -> tgeometry stbox(tgeometry) -> stbox tgeompoint(tgeometry) -> tgeompoint tgeometry(tgeompoint) -> tgeometry centroid(tgeometry) -> tgeometry (temporal centroid path) convexHull(tgeometry) -> geometry traversedArea(tgeometry [, bool]) -> geometry All MEOS exports reached here (tspatial_srid / tspatial_set_srid / tspatial_transform / tspatial_to_stbox / tgeometry_to_tgeompoint / tgeompoint_to_tgeometry / tgeo_centroid / tgeo_convex_hull / tgeo_traversed_area) take a Temporal *, so the new exec helpers in tgeometry_ops.cpp follow the same decode/call/encode pattern as the predicate executors above. Tests: 040_tgeometry_parity.test grew from 33 to 39 assertions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wraps the comprehensive tgeometry parity sweep with three more categories on top of the cumulative base (which already includes the foundational, predicate, and spatial-functions commits plus the prerequisite work from PRs #85 / #86 / #87 / #88): Aggregate wiring (in span_aggregates.cpp + temporal_aggregates.cpp): extent(tgeometry) -> stbox (new tgeometry input via tspatial_extent_transfn) TcountAgg(tgeometry) -> tint MergeAgg(tgeometry) -> tgeometry AppendInstantAgg(tgeometry) -> tgeometry AppendSequenceAgg(tgeometry) -> tgeometry Tile / box emitters (in tgeometry_ops.cpp): spaceBoxes(tgeometry, x, y, z) -> LIST<stbox> spaceTimeBoxes(tgeometry, x, y, z, interval) -> LIST<stbox> Analytics (in tgeometry_ops.cpp) — registered for parity even though MEOS internally rejects non-numeric / non-point temporal inputs at runtime (same constraint as MobilityDB's PG-side): minDistSimplify(tgeometry, double) minTimeDeltaSimplify(tgeometry, interval) maxDistSimplify(tgeometry, double[, bool]) douglasPeuckerSimplify(tgeometry, double[, bool]) Test 040_tgeometry_parity.test grew from 39 to 45 assertions covering aggregates and tile emitters. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the geographic-coordinate-system temporal type tgeography. The MEOS
C functions backing this surface are subtype-agnostic, so the bulk of
this commit is a mechanical mirror of the tgeometry registrations:
search-and-replace TGeometryTypes -> TGeographyTypes, TGEOMETRY()
-> TGEOGRAPHY(), tgeometry_* -> tgeography_*, plus a small number of
case-by-case fixes for the tgeometry <-> tgeography coercions
(tgeogpoint coercions are deferred until tgeogpoint is registered).
Files added:
src/include/geo/tgeography.hpp
src/include/geo/tgeography_ops.hpp
src/geo/tgeography.cpp — type registration, constructors,
foundational accessors, time/value
restrict, modifiers, comparison
src/geo/tgeography_in_out.cpp — text I/O (asText, asEWKT) and
string casts
src/geo/tgeography_ops.cpp — cross-type predicates (boxops,
posops, spatial-rels, temporal-
spatial-rels), distance,
spatial functions, tile / box
emitters, analytics
test/sql/parity/041_tgeography_parity.test — 19 assertions
Aggregate wiring extended in span_aggregates.cpp /
temporal_aggregates.cpp to register tgeography overloads alongside
tgeometry / tgeompoint for extent / TcountAgg / MergeAgg /
AppendInstantAgg / AppendSequenceAgg.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the geographic-coordinate-system point-restricted temporal type
tgeogpoint, completing the cross-product of (geometric, geographic) ×
(generic, point-restricted) temporal types alongside the already-shipped
tgeompoint / tgeometry / tgeography. Like tgeography → tgeometry, this
PR is largely a mechanical mirror: search-and-replace TGeographyTypes ->
TGeogpointType, TGEOGRAPHY() -> TGEOGPOINT(), tgeography_* ->
tgeogpoint_* in MEOS calls. The MEOS C functions backing this surface
are subtype-agnostic between tgeography and tgeogpoint.
Files added:
src/include/geo/tgeogpoint.hpp
src/include/geo/tgeogpoint_ops.hpp
src/geo/tgeogpoint.cpp — type registration, constructors,
foundational accessors, time/value
restrict, modifiers, comparison
src/geo/tgeogpoint_in_out.cpp — text I/O (asText, asEWKT) and casts
src/geo/tgeogpoint_ops.cpp — cross-type predicates (boxops,
posops, spatial-rels, temporal-
spatial-rels), distance, spatial
functions, tile / box emitters,
analytics, plus the
tgeogpoint <-> tgeography coercion
pair (uses
tgeogpoint_to_tgeography and
tgeography_to_tgeogpoint MEOS
exports)
test/sql/parity/042_tgeogpoint_parity.test — 19 assertions
Aggregate wiring extended in span_aggregates.cpp /
temporal_aggregates.cpp to register tgeogpoint overloads alongside
tgeography / tgeometry / tgeompoint for extent / TcountAgg / MergeAgg /
AppendInstantAgg / AppendSequenceAgg, plus TcentroidAgg(tgeogpoint)
since tgeogpoint is point-typed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Wasm CI (wasm32-emscripten) tripped on `static_assert(sizeof(Datum) == sizeof(double))` in Float8ToDatum. Datum is `uintptr_t` — 8 bytes on 64-bit native, 4 bytes on Wasm32 — so the bit-pattern reinterpret can't store a double on Wasm. Mirror PostgreSQL's `Float8GetDatum` shape: bit-cast on 64-bit Datum builds, heap-allocate-and-pass-pointer on 32-bit Datum builds (MEOS' set machinery copies the pointed-at value into the Set on insert and frees it via set_free). The `if` is a constant expression; the unused branch compiles away on each target. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 1, 2026
Member
Author
|
Superseded by the consolidated PR branch |
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the geographic-coordinate-system point-restricted temporal type
tgeogpoint, completing the (geometric / geographic) × (generic / point-restricted) cross-product alongside the already-shippedtgeompoint,tgeometry, andtgeography(PR #91).The MEOS C functions backing this surface are subtype-agnostic between tgeography and tgeogpoint, so this PR is largely a search-and-replace mirror of PR #91 (
tgeography):TGeographyTypes→TGeogpointType,TGEOGRAPHY()→TGEOGPOINT(),tgeography_*→tgeogpoint_*in MEOS calls.What's included
src/geo/tgeogpoint.cpp+tgeogpoint_in_out.cpptgeogpointtype,tgeogpointInst/tgeogpointSeqconstructors,asText/asEWKTsrc/geo/tgeogpoint.cppsrc/geo/tgeogpoint_ops.cppsrc/geo/tgeogpoint_ops.cppSRID,setSRID,transform,stbox,tgeography(tgeogpoint)/tgeogpoint(tgeography)coercions (the pair that PR #91 deferred),centroid,convexHull,traversedAreasrc/temporal/span_aggregates.cpp,src/temporal/temporal_aggregates.cppextent(tgeogpoint)→ stbox;TcountAgg(tgeogpoint)→ tint;MergeAgg(tgeogpoint);AppendInstantAgg(tgeogpoint);AppendSequenceAgg(tgeogpoint);TcentroidAgg(tgeogpoint)(tgeogpoint is a point type, so the temporal centroid aggregate applies)src/geo/tgeogpoint_ops.cppspaceBoxes,spaceTimeBoxes, the four*SimplifyregistrationsStacked on top of #91
This PR is targeted to merge into
feat/parity-tgeography(PR #91). It depends on the tgeography type being registered so thetgeogpoint <-> tgeographycoercion pair can land here.Closes the deferred coercion gap
PR #91's body called out that the
tgeogpointcoercion pair couldn't be registered until tgeogpoint was a registered type. This PR adds them:tgeography(tgeogpoint) → tgeographyviatgeogpoint_to_tgeographytgeogpoint(tgeography) → tgeogpointviatgeography_to_tgeogpointDefaults / quirks
Same as tgeography: SRID defaults to 4326 (WGS84),
extent(tgeogpoint)emitsSRID=4326;GEODSTBOX, encoded payloads start with0101000020E6100000….Out of scope
asEWKB/asHexEWKB/asMFJSON/ parsers) — depends on PR feat(bindings): tgeompoint round-trip I/O — Binary/EWKB/HexWKB/HexEWKB/MFJSON #83 landing first.tgeompoint <-> tgeogpointcoercion — MEOS doesn't export it directly; users can chain viatgeometry/tgeography.Test plan
test/sql/parity/042_tgeogpoint_parity.test— 19 assertions covering accessors, time-domain restrict, modifiers, comparison, box predicates, position predicates, spatial functions (SRID + coercions), and aggregate wiring (includingTcentroidAgg).🤖 Generated with Claude Code