Skip to main content

nodedb_query/geo_functions/catalog/
spec.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Description of one geospatial SQL function: every spelling that reaches it,
4//! the shape of its argument list, and the kind of value it returns.
5
6/// The argument list a geo function accepts.
7///
8/// This is the only place arity is declared. `nodedb-sql` maps each shape to
9/// its `ArgTypeSpec` slice exhaustively, so a new shape cannot be introduced
10/// without the planner being taught the types that go with it.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum GeoArgShape {
13    /// `(geom)`
14    Geometry1,
15    /// `(geom1, geom2)`
16    Geometry2,
17    /// `(geom1, geom2, distance_m)`
18    Geometry2Distance,
19    /// `(geom, distance_m [, segments])`
20    GeometryBuffer,
21    /// `(lng, lat)`
22    LngLat,
23    /// `(x, y [, z])`
24    LngLatZ,
25    /// `(lng, lat [, precision])`
26    LngLatPrecision,
27    /// `(lat, lng, resolution)` — note the reversed leading pair relative to
28    /// [`GeoArgShape::LngLatPrecision`]; the H3 surface names latitude first.
29    LatLngResolution,
30    /// `(text)`
31    Text1,
32    /// `(wkt [, srid])`
33    TextSrid,
34    /// `(wkb [, srid])`
35    BytesSrid,
36    /// `(lng1, lat1, lng2, lat2)`
37    Haversine4,
38    /// `(lng, lat, radius_m [, segments])`
39    Circle,
40    /// `(min_lng, min_lat, max_lng, max_lat)`
41    Bbox,
42    /// `(point, point, ...)`
43    PointsVariadic,
44    /// `(ring, ring, ...)`
45    RingsVariadic,
46}
47
48impl GeoArgShape {
49    /// Minimum and maximum accepted argument counts.
50    pub const fn arity(self) -> (usize, usize) {
51        match self {
52            Self::Geometry1 | Self::Text1 => (1, 1),
53            Self::TextSrid | Self::BytesSrid => (1, 2),
54            Self::Geometry2 | Self::LngLat => (2, 2),
55            Self::GeometryBuffer | Self::LngLatZ | Self::LngLatPrecision => (2, 3),
56            Self::Geometry2Distance | Self::LatLngResolution => (3, 3),
57            Self::Circle => (3, 4),
58            Self::Haversine4 | Self::Bbox => (4, 4),
59            Self::PointsVariadic => (2, MAX_VARIADIC),
60            Self::RingsVariadic => (1, MAX_VARIADIC),
61        }
62    }
63}
64
65/// Upper bound on a variadic geo constructor's argument count. Matches the
66/// registry's existing cap for `geo_line` / `geo_polygon`.
67pub const MAX_VARIADIC: usize = 255;
68
69/// The kind of value a geo function produces.
70///
71/// `nodedb-sql` maps this to a `ColumnType` for plan-time typing. `Object`
72/// covers the decoded-bounds shapes (geohash / H3 cell), which have no scalar
73/// column type and are typed as unknown at plan time.
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum GeoReturn {
76    Bool,
77    Float,
78    Int,
79    Text,
80    Geometry,
81    Object,
82    Array,
83}
84
85/// One geospatial SQL function.
86#[derive(Debug, Clone, Copy)]
87pub struct GeoFunctionSpec {
88    /// Dispatch key. The evaluator matches on exactly this string, and every
89    /// other spelling in `aliases` resolves to it before dispatch.
90    pub canonical: &'static str,
91    /// Additional accepted spellings — the internal `geo_*` names that predate
92    /// the standard ones, plus legacy synonyms. Never includes `canonical`.
93    pub aliases: &'static [&'static str],
94    pub args: GeoArgShape,
95    pub returns: GeoReturn,
96}
97
98impl GeoFunctionSpec {
99    /// Every spelling that resolves to this function, canonical first.
100    pub fn names(&self) -> impl Iterator<Item = &'static str> + '_ {
101        std::iter::once(self.canonical).chain(self.aliases.iter().copied())
102    }
103
104    /// Whether `name` (already lowercased) refers to this function.
105    pub fn matches(&self, name: &str) -> bool {
106        self.canonical == name || self.aliases.contains(&name)
107    }
108}
109
110/// Compact constructor for the catalog table.
111pub(super) const fn f(
112    canonical: &'static str,
113    aliases: &'static [&'static str],
114    args: GeoArgShape,
115    returns: GeoReturn,
116) -> GeoFunctionSpec {
117    GeoFunctionSpec {
118        canonical,
119        aliases,
120        args,
121        returns,
122    }
123}