From 682403d13b66322177eb32c470c6ca415f460c1b Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 11 Jun 2026 00:34:37 +0200 Subject: [PATCH 1/2] feat(tfloat): bind sin/cos/tan as overloads on TFLOAT; bump MEOS pin to 59ba0ad59c Wire `tfloat_sin`, `tfloat_cos`, `tfloat_tan` from MEOS as `sin(tfloat)`, `cos(tfloat)`, `tan(tfloat)` DuckDB scalar overloads, following the same `TemporalUnary` pattern as `exp`/`ln`/`log10`. The pin advances from `278863520b` to `59ba0ad59c` (tag `ecosystem-pin-2026-06-11a`), which adds these three trig kernels and the `ensure_srid_reconcile` helper. --- src/include/temporal/temporal_functions.hpp | 3 ++ src/temporal/temporal.cpp | 3 ++ src/temporal/temporal_functions.cpp | 12 +++++ test/sql/parity/026c_tfloat_trig.test | 56 +++++++++++++++++++++ vcpkg_ports/meos/portfile.cmake | 4 +- 5 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 test/sql/parity/026c_tfloat_trig.test diff --git a/src/include/temporal/temporal_functions.hpp b/src/include/temporal/temporal_functions.hpp index bbb895c3..6a791493 100644 --- a/src/include/temporal/temporal_functions.hpp +++ b/src/include/temporal/temporal_functions.hpp @@ -232,6 +232,9 @@ struct TemporalFunctions { static void Tfloat_exp(DataChunk &args, ExpressionState &state, Vector &result); static void Tfloat_ln(DataChunk &args, ExpressionState &state, Vector &result); static void Tfloat_log10(DataChunk &args, ExpressionState &state, Vector &result); + static void Tfloat_sin(DataChunk &args, ExpressionState &state, Vector &result); + static void Tfloat_cos(DataChunk &args, ExpressionState &state, Vector &result); + static void Tfloat_tan(DataChunk &args, ExpressionState &state, Vector &result); // Temporal_derivative declared in the math-functions block below. static void Tfloat_degrees(DataChunk &args, ExpressionState &state, Vector &result); static void Tfloat_radians(DataChunk &args, ExpressionState &state, Vector &result); diff --git a/src/temporal/temporal.cpp b/src/temporal/temporal.cpp index 9abf2973..b5c59d9e 100644 --- a/src/temporal/temporal.cpp +++ b/src/temporal/temporal.cpp @@ -1367,6 +1367,9 @@ void TemporalTypes::RegisterScalarFunctions(ExtensionLoader &loader) { duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("exp", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_exp)); duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("ln", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_ln)); duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("log10", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_log10)); + duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("sin", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_sin)); + duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("cos", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_cos)); + duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("tan", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_tan)); // deltaValue / trend on tnumber duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("deltaValue", {TemporalTypes::TINT()}, TemporalTypes::TINT(), TemporalFunctions::Tnumber_delta_value)); diff --git a/src/temporal/temporal_functions.cpp b/src/temporal/temporal_functions.cpp index fc8a2b68..5b110e00 100644 --- a/src/temporal/temporal_functions.cpp +++ b/src/temporal/temporal_functions.cpp @@ -4816,6 +4816,18 @@ void TemporalFunctions::Tfloat_log10(DataChunk &args, ExpressionState &state, Ve TemporalUnary(args, result, [](Temporal *t) { return tfloat_log10(t); }); } +void TemporalFunctions::Tfloat_sin(DataChunk &args, ExpressionState &state, Vector &result) { + TemporalUnary(args, result, [](Temporal *t) { return tfloat_sin(t); }); +} + +void TemporalFunctions::Tfloat_cos(DataChunk &args, ExpressionState &state, Vector &result) { + TemporalUnary(args, result, [](Temporal *t) { return tfloat_cos(t); }); +} + +void TemporalFunctions::Tfloat_tan(DataChunk &args, ExpressionState &state, Vector &result) { + TemporalUnary(args, result, [](Temporal *t) { return tfloat_tan(t); }); +} + namespace { template diff --git a/test/sql/parity/026c_tfloat_trig.test b/test/sql/parity/026c_tfloat_trig.test new file mode 100644 index 00000000..1ede88d3 --- /dev/null +++ b/test/sql/parity/026c_tfloat_trig.test @@ -0,0 +1,56 @@ +# name: test/sql/parity/026c_tfloat_trig.test +# description: Trigonometric lifts for tfloat — sin, cos, tan. +# group: [sql] + +require mobilityduck + +# Instant: sin(0) = 0 +query I +SELECT sin(tfloat '[0@2000-01-01]'); +---- +[0@2000-01-01 00:00:00+01] + +# Instant: cos(0) = 1 +query I +SELECT cos(tfloat '[0@2000-01-01]'); +---- +[1@2000-01-01 00:00:00+01] + +# Instant: tan(0) = 0 +query I +SELECT tan(tfloat '[0@2000-01-01]'); +---- +[0@2000-01-01 00:00:00+01] + +# Sequence endpoints: sin lifted over [0, pi/2] starts at 0 and ends at 1 +query I +SELECT round(startValue(sin(tfloat '[0@2000-01-01, 1.5707963267948966@2000-01-02]')), 6); +---- +0.0 + +query I +SELECT round(endValue(sin(tfloat '[0@2000-01-01, 1.5707963267948966@2000-01-02]')), 6); +---- +1.0 + +# Sequence endpoints: cos lifted over [0, pi/2] starts at 1 and ends at 0 +query I +SELECT round(startValue(cos(tfloat '[0@2000-01-01, 1.5707963267948966@2000-01-02]')), 6); +---- +1.0 + +query I +SELECT round(endValue(cos(tfloat '[0@2000-01-01, 1.5707963267948966@2000-01-02]')), 6); +---- +0.0 + +# Sequence endpoints: tan lifted over [0, pi/4] starts at 0 and ends at 1 +query I +SELECT round(startValue(tan(tfloat '[0@2000-01-01, 0.7853981633974483@2000-01-02]')), 6); +---- +0.0 + +query I +SELECT round(endValue(tan(tfloat '[0@2000-01-01, 0.7853981633974483@2000-01-02]')), 6); +---- +1.0 diff --git a/vcpkg_ports/meos/portfile.cmake b/vcpkg_ports/meos/portfile.cmake index fd0da052..738c9f17 100644 --- a/vcpkg_ports/meos/portfile.cmake +++ b/vcpkg_ports/meos/portfile.cmake @@ -1,8 +1,8 @@ vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO estebanzimanyi/MobilityDB - REF 278863520b000b735cc361beab88387174909ed7 - SHA512 2e617cac4bfed919eee8bd73e35f9b3da8ffd7a51f68104a9497c0d3339dbb1af4a2ec6feab3e8fffb880481badf86c352ad1efb39cab533de19d2c1192a8e5b + REF 59ba0ad59cb93db6fa46929394e475b7851c00be + SHA512 e6a4a1578e5760326a596248865ccb487850120ca423f5675d1f014e9146de4f407c699afa63a102d8ba4ef1f895946d206ee4ab2f870dc4508ec2ca3d771869 ) vcpkg_replace_string( From 2cc91ffe5a270b9dbd383987de93aa44899fe4df Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sun, 7 Jun 2026 14:43:05 +0200 Subject: [PATCH 2/2] feat(stbox): bind expandSpace overloads for geometry, tgeompoint, tgeogpoint, tgeometry, tgeography expandSpace(geometry, double), expandSpace(tgeompoint, double), expandSpace(tgeogpoint, double), expandSpace(tgeometry, double), and expandSpace(tgeography, double) each return an STBOX expanded by the given distance on all spatial axes. The implementation follows the MobilityDB canonical pattern: convert to STBox via geo_to_stbox or tspatial_to_stbox, then delegate to stbox_expand_space. The single kernel symbol stbox_expand_space covers all overloads. Tests for all five new overloads are added to test/sql/stbox.test. --- src/geo/stbox.cpp | 33 +++++++++++- src/geo/stbox_functions.cpp | 78 +++++++++++++++++++++++++++++ src/include/geo/stbox_functions.hpp | 2 + test/sql/stbox.test | 25 +++++++++ 4 files changed, 136 insertions(+), 2 deletions(-) diff --git a/src/geo/stbox.cpp b/src/geo/stbox.cpp index 3b667360..e38f184a 100644 --- a/src/geo/stbox.cpp +++ b/src/geo/stbox.cpp @@ -4,6 +4,9 @@ #include "geo/stbox.hpp" #include "geo/stbox_functions.hpp" #include "geo/tgeompoint.hpp" +#include "geo/tgeogpoint.hpp" +#include "geo/tgeometry.hpp" +#include "geo/tgeography.hpp" #include "duckdb/common/types/blob.hpp" #include "duckdb/function/function.hpp" @@ -377,7 +380,7 @@ void StboxType::RegisterScalarFunctions(ExtensionLoader &loader) { ) ); - duckdb::RegisterSerializedScalarFunction(loader, + duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction( "expandSpace", {STBOX(), LogicalType::DOUBLE}, @@ -386,7 +389,33 @@ void StboxType::RegisterScalarFunctions(ExtensionLoader &loader) { ) ); - duckdb::RegisterSerializedScalarFunction(loader, + duckdb::RegisterSerializedScalarFunction(loader, + ScalarFunction( + "expandSpace", + {GeoTypes::GEOMETRY(), LogicalType::DOUBLE}, + STBOX(), + StboxFunctions::Geo_expand_space + ) + ); + + { + const auto D = LogicalType::DOUBLE; + const auto B = STBOX(); + const auto P = TgeompointType::TGEOMPOINT(); + const auto GP = TgeogpointType::TGEOGPOINT(); + const auto TGM = TGeometryTypes::TGEOMETRY(); + const auto TGG = TGeographyTypes::TGEOGRAPHY(); + duckdb::RegisterSerializedScalarFunction(loader, + ScalarFunction("expandSpace", {P, D}, B, StboxFunctions::Tspatial_expand_space)); + duckdb::RegisterSerializedScalarFunction(loader, + ScalarFunction("expandSpace", {GP, D}, B, StboxFunctions::Tspatial_expand_space)); + duckdb::RegisterSerializedScalarFunction(loader, + ScalarFunction("expandSpace", {TGM, D}, B, StboxFunctions::Tspatial_expand_space)); + duckdb::RegisterSerializedScalarFunction(loader, + ScalarFunction("expandSpace", {TGG, D}, B, StboxFunctions::Tspatial_expand_space)); + } + + duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction( "stbox_contains", {STBOX(), STBOX()}, diff --git a/src/geo/stbox_functions.cpp b/src/geo/stbox_functions.cpp index b5398f02..25347f48 100644 --- a/src/geo/stbox_functions.cpp +++ b/src/geo/stbox_functions.cpp @@ -1419,6 +1419,84 @@ void StboxFunctions::Stbox_expand_space(DataChunk &args, ExpressionState &state, } } +void StboxFunctions::Geo_expand_space(DataChunk &args, ExpressionState &state, Vector &result) { + BinaryExecutor::ExecuteWithNulls( + args.data[0], args.data[1], result, args.size(), + [&](string_t geometry_blob, double d, ValidityMask &mask, idx_t idx) -> string_t { + int32 srid = 0; + GSERIALIZED *gs = GeometryToGSerialized(geometry_blob, srid); + if (!gs) { + throw InvalidInputException("Invalid geometry format in Geo_expand_space"); + } + STBox *box = geo_to_stbox(gs); + free(gs); + if (!box) { + mask.SetInvalid(idx); + return string_t(); + } + STBox *ret = stbox_expand_space(box, d); + free(box); + if (!ret) { + mask.SetInvalid(idx); + return string_t(); + } + size_t stbox_size = sizeof(STBox); + uint8_t *stbox_data = (uint8_t*)malloc(stbox_size); + if (!stbox_data) { + free(ret); + throw InternalException("Failure in Geo_expand_space: unable to allocate memory for stbox"); + } + memcpy(stbox_data, ret, stbox_size); + string_t ret_str(reinterpret_cast(stbox_data), stbox_size); + string_t stored_data = StringVector::AddStringOrBlob(result, ret_str); + free(stbox_data); + free(ret); + return stored_data; + } + ); +} + +void StboxFunctions::Tspatial_expand_space(DataChunk &args, ExpressionState &state, Vector &result) { + BinaryExecutor::ExecuteWithNulls( + args.data[0], args.data[1], result, args.size(), + [&](string_t input_blob, double d, ValidityMask &mask, idx_t idx) -> string_t { + const uint8_t *data = reinterpret_cast(input_blob.GetData()); + size_t data_size = input_blob.GetSize(); + uint8_t *data_copy = (uint8_t*)malloc(data_size); + memcpy(data_copy, data, data_size); + Temporal *temp = reinterpret_cast(data_copy); + if (!temp) { + free(data_copy); + throw InvalidInputException("Invalid temporal value in Tspatial_expand_space"); + } + STBox *box = tspatial_to_stbox(temp); + free(temp); + if (!box) { + mask.SetInvalid(idx); + return string_t(); + } + STBox *ret = stbox_expand_space(box, d); + free(box); + if (!ret) { + mask.SetInvalid(idx); + return string_t(); + } + size_t stbox_size = sizeof(STBox); + uint8_t *stbox_data = (uint8_t*)malloc(stbox_size); + if (!stbox_data) { + free(ret); + throw InternalException("Failure in Tspatial_expand_space: unable to allocate memory for stbox"); + } + memcpy(stbox_data, ret, stbox_size); + string_t ret_str(reinterpret_cast(stbox_data), stbox_size); + string_t stored_data = StringVector::AddStringOrBlob(result, ret_str); + free(stbox_data); + free(ret); + return stored_data; + } + ); +} + /* *************************************************** * Topological operators ****************************************************/ diff --git a/src/include/geo/stbox_functions.hpp b/src/include/geo/stbox_functions.hpp index 2bd041f5..eb10f038 100644 --- a/src/include/geo/stbox_functions.hpp +++ b/src/include/geo/stbox_functions.hpp @@ -89,6 +89,8 @@ struct StboxFunctions { static void Stbox_shift_scale_time(DataChunk &args, ExpressionState &state, Vector &result); static void Stbox_get_space(DataChunk &args, ExpressionState &state, Vector &result); static void Stbox_expand_space(DataChunk &args, ExpressionState &state, Vector &result); + static void Geo_expand_space(DataChunk &args, ExpressionState &state, Vector &result); + static void Tspatial_expand_space(DataChunk &args, ExpressionState &state, Vector &result); static void Stbox_expand_time(DataChunk &args, ExpressionState &state, Vector &result); /* *************************************************** diff --git a/test/sql/stbox.test b/test/sql/stbox.test index 87b4dcc7..a6d96dfa 100644 --- a/test/sql/stbox.test +++ b/test/sql/stbox.test @@ -101,6 +101,31 @@ SELECT expandSpace(stbox 'STBOX XT(((1.0,1.0),(2.0,2.0)),[2000-01-01,2000-01-01] ---- STBOX XT(((1.5,1.5),(1.5,1.5)),[2000-01-01 00:00:00+01, 2000-01-01 00:00:00+01]) +query I +SELECT expandSpace(ST_Point(1.0, 2.0), 1.0); +---- +STBOX X((0,1),(2,3)) + +query I +SELECT expandSpace(tgeompoint '[Point(0 0)@2000-01-01, Point(2 2)@2000-01-02]', 1.0); +---- +STBOX XT(((-1,-1),(3,3)),[2000-01-01 00:00:00+01, 2000-01-02 00:00:00+01]) + +query I +SELECT expandSpace(tgeogpoint '[Point(0 0)@2000-01-01, Point(2 2)@2000-01-02]', 1.0); +---- +SRID=4326;GEODSTBOX XT(((-1,-1),(3,3)),[2000-01-01 00:00:00+01, 2000-01-02 00:00:00+01]) + +query I +SELECT expandSpace(tgeometry '[Point(0 0)@2000-01-01, Point(2 2)@2000-01-02]', 1.0); +---- +STBOX XT(((-1,-1),(3,3)),[2000-01-01 00:00:00+01, 2000-01-02 00:00:00+01]) + +query I +SELECT expandSpace(tgeography '[Point(0 0)@2000-01-01, Point(2 2)@2000-01-02]', 1.0); +---- +SRID=4326;GEODSTBOX XT(((-1,-1),(3,3)),[2000-01-01 00:00:00+01, 2000-01-02 00:00:00+01]) + query I SELECT STBOX(ST_Point(1, 1)); ----