From 59371b139e698b29acdf44b279198d7efcf6d135 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sat, 24 Oct 2026 14:06:55 +0200 Subject: [PATCH] Generate the constructors of the temporal cell indexes The constructors of tquadbin, th3index and ts2cell from a cell and a time value are generated from the catalog: the groups meos_quadbin_constructor, meos_h3_constructor and meos_s2cell_constructor are retired beside meos_temporal_constructor and the other family constructors. Each signature MobilityDB declares registers once, under its own name, argument types and result: tquadbin, th3index and ts2cell over their cell and a timestamptz (tquadbininst_make, th3indexinst_make, ts2cellinst_make), a tstzset and a tstzspan (the *seq_from_base_* functions) and a tstzspanset (the *seqset_from_base_tstzspanset functions). shape_sig_call reads a SQL span set argument as it reads a span: sig_call_arg takes its DuckDB type from SPANSET_TYPES beside the span, base and set types, which reaches the three span set forms and no other signature. The hand tquadbin(quadbin, timestamptz) of src/quadbin/tquadbin.cpp, the generated tquadbininst_make replacing it, is deleted with its executor Tquadbin_make and QuadbinTypes::RegisterScalarFunctions, which held nothing else; the quadbin and tquadbin types and their text casts stay, as for h3index and th3index. Measured: on main th3index(h3index, timestamptz), ts2cell(s2cell, timestamptz) and the span set forms of all three register nothing, hand or generated. The generator emits 12 registrations more, the four constructors of each of the three cell types, and changes no other line of src/generated; a second run reproduces it byte for byte. test/sql/tquadbin.test, th3index.test and ts2cell.test read each constructor against the literal of the answer MobilityDB's regression suite gives for the same call (352_tquadbin, 270_th3index, 603_ts2cell). The build has no warning in the lines this change writes, and the suite passes, 4908 assertions in 121 test cases. --- src/generated/generated_temporal_udfs.cpp | 6 ++ src/generated/generated_temporal_udfs_14.cpp | 87 ++++++++++++++++++++ src/generated/generated_temporal_udfs_2.cpp | 87 ++++++++++++++++++++ src/generated/generated_temporal_udfs_7.cpp | 87 ++++++++++++++++++++ src/include/quadbin/tquadbin.hpp | 7 -- src/mobilityduck_extension.cpp | 1 - src/quadbin/tquadbin.cpp | 49 ++--------- test/sql/th3index.test | 27 ++++++ test/sql/tquadbin.test | 26 ++++++ test/sql/ts2cell.test | 28 +++++++ tools/codegen_duck_udfs.py | 2 + 11 files changed, 355 insertions(+), 52 deletions(-) diff --git a/src/generated/generated_temporal_udfs.cpp b/src/generated/generated_temporal_udfs.cpp index f38b5718..93cb58c1 100644 --- a/src/generated/generated_temporal_udfs.cpp +++ b/src/generated/generated_temporal_udfs.cpp @@ -122,6 +122,7 @@ void RegisterGenerated_meos_h3_base_metrics(ExtensionLoader &loader); void RegisterGenerated_meos_h3_comp(ExtensionLoader &loader); void RegisterGenerated_meos_h3_comp_ever(ExtensionLoader &loader); void RegisterGenerated_meos_h3_comp_temp(ExtensionLoader &loader); +void RegisterGenerated_meos_h3_constructor(ExtensionLoader &loader); void RegisterGenerated_meos_h3_conversion(ExtensionLoader &loader); void RegisterGenerated_meos_h3_edges(ExtensionLoader &loader); void RegisterGenerated_meos_h3_hierarchy(ExtensionLoader &loader); @@ -230,6 +231,7 @@ void RegisterGenerated_meos_quadbin_base_comp(ExtensionLoader &loader); void RegisterGenerated_meos_quadbin_base_inout(ExtensionLoader &loader); void RegisterGenerated_meos_quadbin_comp_ever(ExtensionLoader &loader); void RegisterGenerated_meos_quadbin_comp_temp(ExtensionLoader &loader); +void RegisterGenerated_meos_quadbin_constructor(ExtensionLoader &loader); void RegisterGenerated_meos_quadbin_conversion(ExtensionLoader &loader); void RegisterGenerated_meos_quadbin_inout(ExtensionLoader &loader); void RegisterGenerated_meos_quadbin_restrict(ExtensionLoader &loader); @@ -265,6 +267,7 @@ void RegisterGenerated_meos_s2cell_base_comp(ExtensionLoader &loader); void RegisterGenerated_meos_s2cell_base_inout(ExtensionLoader &loader); void RegisterGenerated_meos_s2cell_comp_ever(ExtensionLoader &loader); void RegisterGenerated_meos_s2cell_comp_temp(ExtensionLoader &loader); +void RegisterGenerated_meos_s2cell_constructor(ExtensionLoader &loader); void RegisterGenerated_meos_s2cell_conversion(ExtensionLoader &loader); void RegisterGenerated_meos_s2cell_inout(ExtensionLoader &loader); void RegisterGenerated_meos_s2cell_restrict(ExtensionLoader &loader); @@ -385,6 +388,7 @@ void RegisterGeneratedTemporalUdfs(ExtensionLoader &loader) { RegisterGenerated_meos_h3_comp(loader); RegisterGenerated_meos_h3_comp_ever(loader); RegisterGenerated_meos_h3_comp_temp(loader); + RegisterGenerated_meos_h3_constructor(loader); RegisterGenerated_meos_h3_conversion(loader); RegisterGenerated_meos_h3_edges(loader); RegisterGenerated_meos_h3_hierarchy(loader); @@ -493,6 +497,7 @@ void RegisterGeneratedTemporalUdfs(ExtensionLoader &loader) { RegisterGenerated_meos_quadbin_base_inout(loader); RegisterGenerated_meos_quadbin_comp_ever(loader); RegisterGenerated_meos_quadbin_comp_temp(loader); + RegisterGenerated_meos_quadbin_constructor(loader); RegisterGenerated_meos_quadbin_conversion(loader); RegisterGenerated_meos_quadbin_inout(loader); RegisterGenerated_meos_quadbin_restrict(loader); @@ -528,6 +533,7 @@ void RegisterGeneratedTemporalUdfs(ExtensionLoader &loader) { RegisterGenerated_meos_s2cell_base_inout(loader); RegisterGenerated_meos_s2cell_comp_ever(loader); RegisterGenerated_meos_s2cell_comp_temp(loader); + RegisterGenerated_meos_s2cell_constructor(loader); RegisterGenerated_meos_s2cell_conversion(loader); RegisterGenerated_meos_s2cell_inout(loader); RegisterGenerated_meos_s2cell_restrict(loader); diff --git a/src/generated/generated_temporal_udfs_14.cpp b/src/generated/generated_temporal_udfs_14.cpp index 20114777..badc3784 100644 --- a/src/generated/generated_temporal_udfs_14.cpp +++ b/src/generated/generated_temporal_udfs_14.cpp @@ -2856,6 +2856,86 @@ static void Gen_tne_ts2cell_s2cell(DataChunk &args, ExpressionState &, Vector &r } +// ===== @ingroup meos_s2cell_constructor ===== +static void Gen_ts2cellinst_make(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + Temporal *r = (Temporal *) ts2cellinst_make(static_cast(in0[i]), DuckDBToMeosTimestamp(in1[i]).value); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + +static void Gen_ts2cellseq_from_base_tstzset(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + Set *p1 = BlobToSet(in1[i]); + Temporal *r = (Temporal *) ts2cellseq_from_base_tstzset(static_cast(in0[i]), p1); + free(p1); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + +static void Gen_ts2cellseq_from_base_tstzspan(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + Span *p1 = BlobToSpan(in1[i]); + Temporal *r = (Temporal *) ts2cellseq_from_base_tstzspan(static_cast(in0[i]), p1); + free(p1); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + +static void Gen_ts2cellseqset_from_base_tstzspanset(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + SpanSet *p1 = BlobToSpanSet(in1[i]); + Temporal *r = (Temporal *) ts2cellseqset_from_base_tstzspanset(static_cast(in0[i]), p1); + free(p1); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + + // ===== @ingroup meos_s2cell_conversion ===== static void Gen_bigint_to_s2cell(DataChunk &args, ExpressionState &, Vector &result) { EnsureMeosThreadInitialized(); @@ -3163,6 +3243,13 @@ void RegisterGenerated_meos_s2cell_comp_temp(ExtensionLoader &loader) { RegisterSerializedScalarFunction(loader, ScalarFunction("tNotEqual", {S2cellTypes::ts2cell(), S2cellTypes::s2cell()}, TemporalTypes::tbool(), Gen_tne_ts2cell_s2cell)); } +void RegisterGenerated_meos_s2cell_constructor(ExtensionLoader &loader) { + RegisterSerializedScalarFunction(loader, ScalarFunction("ts2cell", {S2cellTypes::s2cell(), LogicalType::TIMESTAMP_TZ}, S2cellTypes::ts2cell(), Gen_ts2cellinst_make)); + RegisterSerializedScalarFunction(loader, ScalarFunction("ts2cell", {S2cellTypes::s2cell(), SetTypes::tstzset()}, S2cellTypes::ts2cell(), Gen_ts2cellseq_from_base_tstzset)); + RegisterSerializedScalarFunction(loader, ScalarFunction("ts2cell", {S2cellTypes::s2cell(), SpanTypes::tstzspan()}, S2cellTypes::ts2cell(), Gen_ts2cellseq_from_base_tstzspan)); + RegisterSerializedScalarFunction(loader, ScalarFunction("ts2cell", {S2cellTypes::s2cell(), SpansetTypes::tstzspanset()}, S2cellTypes::ts2cell(), Gen_ts2cellseqset_from_base_tstzspanset)); +} + void RegisterGenerated_meos_s2cell_conversion(ExtensionLoader &loader) { RegisterSerializedScalarFunction(loader, ScalarFunction("s2cell", {LogicalType::BIGINT}, S2cellTypes::s2cell(), Gen_bigint_to_s2cell)); RegisterSerializedScalarFunction(loader, ScalarFunction("s2cell_recv", {LogicalType::BIGINT}, S2cellTypes::s2cell(), Gen_bigint_to_s2cell)); diff --git a/src/generated/generated_temporal_udfs_2.cpp b/src/generated/generated_temporal_udfs_2.cpp index a432e8d6..61e232af 100644 --- a/src/generated/generated_temporal_udfs_2.cpp +++ b/src/generated/generated_temporal_udfs_2.cpp @@ -2072,6 +2072,86 @@ static void Gen_right_tspatial_stbox(DataChunk &args, ExpressionState &, Vector } +// ===== @ingroup meos_h3_constructor ===== +static void Gen_th3indexinst_make(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + Temporal *r = (Temporal *) th3indexinst_make(static_cast(in0[i]), DuckDBToMeosTimestamp(in1[i]).value); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + +static void Gen_th3indexseq_from_base_tstzset(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + Set *p1 = BlobToSet(in1[i]); + Temporal *r = (Temporal *) th3indexseq_from_base_tstzset(static_cast(in0[i]), p1); + free(p1); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + +static void Gen_th3indexseq_from_base_tstzspan(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + Span *p1 = BlobToSpan(in1[i]); + Temporal *r = (Temporal *) th3indexseq_from_base_tstzspan(static_cast(in0[i]), p1); + free(p1); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + +static void Gen_th3indexseqset_from_base_tstzspanset(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + SpanSet *p1 = BlobToSpanSet(in1[i]); + Temporal *r = (Temporal *) th3indexseqset_from_base_tstzspanset(static_cast(in0[i]), p1); + free(p1); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + + // ===== @ingroup meos_h3_set_setops ===== static void Gen_contained_h3index_set(DataChunk &args, ExpressionState &, Vector &result) { EnsureMeosThreadInitialized(); @@ -5551,6 +5631,13 @@ void RegisterGenerated_meos_geo_bbox_pos(ExtensionLoader &loader) { RegisterSerializedScalarFunction(loader, ScalarFunction(">>", {PosechainTypes::tposechain(), StboxType::stbox()}, LogicalType::BOOLEAN, Gen_right_tspatial_stbox)); } +void RegisterGenerated_meos_h3_constructor(ExtensionLoader &loader) { + RegisterSerializedScalarFunction(loader, ScalarFunction("th3index", {H3indexTypes::h3index(), LogicalType::TIMESTAMP_TZ}, H3indexTypes::th3index(), Gen_th3indexinst_make)); + RegisterSerializedScalarFunction(loader, ScalarFunction("th3index", {H3indexTypes::h3index(), SetTypes::tstzset()}, H3indexTypes::th3index(), Gen_th3indexseq_from_base_tstzset)); + RegisterSerializedScalarFunction(loader, ScalarFunction("th3index", {H3indexTypes::h3index(), SpanTypes::tstzspan()}, H3indexTypes::th3index(), Gen_th3indexseq_from_base_tstzspan)); + RegisterSerializedScalarFunction(loader, ScalarFunction("th3index", {H3indexTypes::h3index(), SpansetTypes::tstzspanset()}, H3indexTypes::th3index(), Gen_th3indexseqset_from_base_tstzspanset)); +} + void RegisterGenerated_meos_h3_set_setops(ExtensionLoader &loader) { RegisterSerializedScalarFunction(loader, ScalarFunction("setContained", {H3indexTypes::h3index(), SetTypes::h3indexset()}, LogicalType::BOOLEAN, Gen_contained_h3index_set)); RegisterSerializedScalarFunction(loader, ScalarFunction("<@", {H3indexTypes::h3index(), SetTypes::h3indexset()}, LogicalType::BOOLEAN, Gen_contained_h3index_set)); diff --git a/src/generated/generated_temporal_udfs_7.cpp b/src/generated/generated_temporal_udfs_7.cpp index 27154a08..96d46ed6 100644 --- a/src/generated/generated_temporal_udfs_7.cpp +++ b/src/generated/generated_temporal_udfs_7.cpp @@ -1653,6 +1653,86 @@ static void Gen_pose_as_text_d(DataChunk &args, ExpressionState &, Vector &resul } +// ===== @ingroup meos_quadbin_constructor ===== +static void Gen_tquadbininst_make(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + Temporal *r = (Temporal *) tquadbininst_make(static_cast(in0[i]), DuckDBToMeosTimestamp(in1[i]).value); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + +static void Gen_tquadbinseq_from_base_tstzset(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + Set *p1 = BlobToSet(in1[i]); + Temporal *r = (Temporal *) tquadbinseq_from_base_tstzset(static_cast(in0[i]), p1); + free(p1); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + +static void Gen_tquadbinseq_from_base_tstzspan(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + Span *p1 = BlobToSpan(in1[i]); + Temporal *r = (Temporal *) tquadbinseq_from_base_tstzspan(static_cast(in0[i]), p1); + free(p1); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + +static void Gen_tquadbinseqset_from_base_tstzspanset(DataChunk &args, ExpressionState &, Vector &result) { + EnsureMeosThreadInitialized(); + const idx_t rows = args.size(); + for (idx_t c = 0; c < 2; c++) args.data[c].Flatten(rows); + auto in0 = FlatVector::GetData(args.data[0]); + auto in1 = FlatVector::GetData(args.data[1]); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t i = 0; i < rows; i++) { + if (!FlatVector::Validity(args.data[0]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + if (!FlatVector::Validity(args.data[1]).RowIsValid(i)) { outv.SetInvalid(i); continue; } + SpanSet *p1 = BlobToSpanSet(in1[i]); + Temporal *r = (Temporal *) tquadbinseqset_from_base_tstzspanset(static_cast(in0[i]), p1); + free(p1); + if (!r) { outv.SetInvalid(i); continue; } + out[i] = TemporalToBlob(result, r); + } + if (rows == 1) result.SetVectorType(VectorType::CONSTANT_VECTOR); +} + + // ===== @ingroup meos_quadbin_restrict ===== static void Gen_tquadbin_at_value(DataChunk &args, ExpressionState &, Vector &result) { EnsureMeosThreadInitialized(); @@ -1841,6 +1921,13 @@ void RegisterGenerated_meos_pose_base_inout(ExtensionLoader &loader) { RegisterSerializedScalarFunction(loader, ScalarFunction("asText", {PoseTypes::pose()}, LogicalType::VARCHAR, Gen_pose_as_text_d)); } +void RegisterGenerated_meos_quadbin_constructor(ExtensionLoader &loader) { + RegisterSerializedScalarFunction(loader, ScalarFunction("tquadbin", {QuadbinTypes::quadbin(), LogicalType::TIMESTAMP_TZ}, QuadbinTypes::tquadbin(), Gen_tquadbininst_make)); + RegisterSerializedScalarFunction(loader, ScalarFunction("tquadbin", {QuadbinTypes::quadbin(), SetTypes::tstzset()}, QuadbinTypes::tquadbin(), Gen_tquadbinseq_from_base_tstzset)); + RegisterSerializedScalarFunction(loader, ScalarFunction("tquadbin", {QuadbinTypes::quadbin(), SpanTypes::tstzspan()}, QuadbinTypes::tquadbin(), Gen_tquadbinseq_from_base_tstzspan)); + RegisterSerializedScalarFunction(loader, ScalarFunction("tquadbin", {QuadbinTypes::quadbin(), SpansetTypes::tstzspanset()}, QuadbinTypes::tquadbin(), Gen_tquadbinseqset_from_base_tstzspanset)); +} + void RegisterGenerated_meos_quadbin_restrict(ExtensionLoader &loader) { RegisterSerializedScalarFunction(loader, ScalarFunction("atValue", {QuadbinTypes::tquadbin(), QuadbinTypes::quadbin()}, QuadbinTypes::tquadbin(), Gen_tquadbin_at_value)); RegisterSerializedScalarFunction(loader, ScalarFunction("minusValue", {QuadbinTypes::tquadbin(), QuadbinTypes::quadbin()}, QuadbinTypes::tquadbin(), Gen_tquadbin_minus_value)); diff --git a/src/include/quadbin/tquadbin.hpp b/src/include/quadbin/tquadbin.hpp index e51dd4cb..b8a8dcdb 100644 --- a/src/include/quadbin/tquadbin.hpp +++ b/src/include/quadbin/tquadbin.hpp @@ -19,7 +19,6 @@ struct QuadbinTypes { static void RegisterTypes(ExtensionLoader &loader); static void RegisterCastFunctions(ExtensionLoader &loader); - static void RegisterScalarFunctions(ExtensionLoader &loader); }; struct QuadbinFunctions { @@ -30,12 +29,6 @@ struct QuadbinFunctions { /* In/out — TQUADBIN temporal value */ static bool Tquadbin_in_cast(Vector &source, Vector &result, idx_t count, CastParameters ¶meters); static bool Tquadbin_out_cast(Vector &source, Vector &result, idx_t count, CastParameters ¶meters); - - /* Constructor */ - static void Tquadbin_make(DataChunk &args, ExpressionState &state, Vector &result); - - /* Accessors (startValue/endValue/valueN/getValues are generated — see src/generated) */ - }; } // namespace duckdb diff --git a/src/mobilityduck_extension.cpp b/src/mobilityduck_extension.cpp index 5b03c0d1..32618226 100644 --- a/src/mobilityduck_extension.cpp +++ b/src/mobilityduck_extension.cpp @@ -430,7 +430,6 @@ static void LoadInternal(ExtensionLoader &loader) { QuadbinTypes::RegisterTypes(loader); QuadbinTypes::RegisterCastFunctions(loader); - QuadbinTypes::RegisterScalarFunctions(loader); S2cellTypes::RegisterTypes(loader); S2cellTypes::RegisterCastFunctions(loader); diff --git a/src/quadbin/tquadbin.cpp b/src/quadbin/tquadbin.cpp index 0f463f6d..8a78bbff 100644 --- a/src/quadbin/tquadbin.cpp +++ b/src/quadbin/tquadbin.cpp @@ -1,11 +1,13 @@ /* MobilityDuck binding for the MEOS QUADBIN cell index types (quadbin + - * tquadbin). Wraps every implemented export from `meos_quadbin.h` so DuckDB - * SQL can call the full static-cell and temporal-cell surface. + * tquadbin). * * QUADBIN is surfaced as BIGINT (the 64-bit cell id reinterprets losslessly). * TQUADBIN is the temporal cell index, stored as a Temporal* blob (BLOB). * The analogue of H3INDEX / TH3INDEX for the square Web-Mercator DGGS. - */ + * + * Only the TYPE registration + text I/O casts are hand-written here, as for + * H3INDEX/TH3INDEX. The QUADBIN function surface is generated from the + * MEOS-API catalog. */ #include "quadbin/tquadbin.hpp" #include "temporal/temporal.hpp" @@ -144,45 +146,4 @@ bool QuadbinFunctions::Tquadbin_out_cast( return true; } -/* ===================================================================== - * Constructor — tquadbininst_make wrapped as `tquadbin(cell, t)` - * ===================================================================== */ - -void QuadbinFunctions::Tquadbin_make( - DataChunk &args, ExpressionState &state, Vector &result) -{ - BinaryExecutor::Execute( - args.data[0], args.data[1], result, args.size(), - [&](int64_t cell, timestamp_tz_t t) -> string_t { - TInstant *inst = tquadbininst_make( - static_cast(cell), ToMeosTimestamp(t)); - return TempToBlob(result, reinterpret_cast(inst)); - }); -} - -/* ===================================================================== - * Registration - * ===================================================================== */ - -void QuadbinTypes::RegisterScalarFunctions(ExtensionLoader &loader) { - const auto QB = QuadbinTypes::quadbin(); - const auto TQB = QuadbinTypes::tquadbin(); - const auto B = LogicalType::BOOLEAN; - const auto V = LogicalType::VARCHAR; - const auto TS = LogicalType::TIMESTAMP_TZ; - - /* Constructor */ - duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction( - "tquadbin", {QB, TS}, TQB, - QuadbinFunctions::Tquadbin_make)); - - /* Accessors — startValue/endValue, valueN, getValues and valueAtTimestamp are generated - * from the catalog (meos_quadbin_accessor). */ - - /* Quadkey conversion */ - - /* tbigint ↔ tquadbin: deferred until TemporalTypes::TBIGINT() is published, - * same as the th3index binding. */ -} - } // namespace duckdb diff --git a/test/sql/th3index.test b/test/sql/th3index.test index 6f47a429..28f75dbf 100644 --- a/test/sql/th3index.test +++ b/test/sql/th3index.test @@ -154,3 +154,30 @@ query I SELECT valueAtTimestamp(th3index '[8928308280fffff@2000-01-01, 8928308280fffff@2000-01-02]', TIMESTAMPTZ '2000-01-01 12:00:00')::VARCHAR; ---- 8928308280fffff + +# the constructors from a cell and a time value, as MobilityDB answers them +# (270_th3index.test.out); each is read against the literal of that answer + +query I +SELECT asText(th3index(h3index '880326b885fffff', TIMESTAMPTZ '2001-01-01 08:00')) + = asText(th3index '880326b885fffff@2001-01-01 08:00'); +---- +true + +query I +SELECT asText(th3index(h3index '880326b885fffff', tstzset '{2001-01-01, 2001-01-02}')) + = asText(th3index '{880326b885fffff@2001-01-01, 880326b885fffff@2001-01-02}'); +---- +true + +query I +SELECT asText(th3index(h3index '880326b885fffff', tstzspan '[2001-01-01, 2001-01-02]')) + = asText(th3index '[880326b885fffff@2001-01-01, 880326b885fffff@2001-01-02]'); +---- +true + +query I +SELECT asText(th3index(h3index '880326b885fffff', tstzspanset '{[2001-01-01, 2001-01-02]}')) + = asText(th3index '{[880326b885fffff@2001-01-01, 880326b885fffff@2001-01-02]}'); +---- +true diff --git a/test/sql/tquadbin.test b/test/sql/tquadbin.test index ea6c1b68..515eab46 100644 --- a/test/sql/tquadbin.test +++ b/test/sql/tquadbin.test @@ -183,3 +183,29 @@ query II SELECT asText(valueAtTimestamp(tquadbin(quadbinTileToCell(1,0,1), TIMESTAMPTZ '2024-01-01'), TIMESTAMPTZ '2024-01-01')), typeof(valueAtTimestamp(tquadbin(quadbinTileToCell(1,0,1), TIMESTAMPTZ '2024-01-01'), TIMESTAMPTZ '2024-01-01')); ---- 4817ffffffffffff quadbin + +# ── the constructors from a cell and a time value, as MobilityDB answers them +# (352_tquadbin.test.out); each is read against the literal of that answer ── +query I +SELECT asText(tquadbin(quadbin '480fffffffffffff', TIMESTAMPTZ '2001-01-01 08:00')) + = asText(tquadbin '480fffffffffffff@2001-01-01 08:00'); +---- +true + +query I +SELECT asText(tquadbin(quadbin '48a6227affffffff', tstzset '{2001-01-01, 2001-01-02}')) + = asText(tquadbin '{48a6227affffffff@2001-01-01, 48a6227affffffff@2001-01-02}'); +---- +true + +query I +SELECT asText(tquadbin(quadbin '48a6227affffffff', tstzspan '[2001-01-01, 2001-01-02]')) + = asText(tquadbin '[48a6227affffffff@2001-01-01, 48a6227affffffff@2001-01-02]'); +---- +true + +query I +SELECT asText(tquadbin(quadbin '48a6227affffffff', tstzspanset '{[2001-01-01, 2001-01-02]}')) + = asText(tquadbin '{[48a6227affffffff@2001-01-01, 48a6227affffffff@2001-01-02]}'); +---- +true diff --git a/test/sql/ts2cell.test b/test/sql/ts2cell.test index 21b09a0d..b330b6ae 100644 --- a/test/sql/ts2cell.test +++ b/test/sql/ts2cell.test @@ -92,3 +92,31 @@ query I SELECT valueAtTimestamp(ts2cell '[89c25a3@2000-01-01, 89c25a3@2000-01-02]', TIMESTAMPTZ '2000-01-01 12:00:00')::VARCHAR; ---- 89c25a3 + +# the constructors from a cell and a time value, as MobilityDB answers the +# instant and the span forms (603_ts2cell.test.out); each is read against the +# literal of its answer + +query I +SELECT asText(ts2cell(s2cell '47c3c3', TIMESTAMPTZ '2001-01-01')) + = asText(ts2cell '47c3c3@2001-01-01'); +---- +true + +query I +SELECT asText(ts2cell(s2cell '47c3c3', tstzset '{2001-01-01, 2001-01-02}')) + = asText(ts2cell '{47c3c3@2001-01-01, 47c3c3@2001-01-02}'); +---- +true + +query I +SELECT asText(ts2cell(s2cell '47c3c3', tstzspan '[2001-01-01, 2001-01-02]')) + = asText(ts2cell '[47c3c3@2001-01-01, 47c3c3@2001-01-02]'); +---- +true + +query I +SELECT asText(ts2cell(s2cell '47c3c3', tstzspanset '{[2001-01-01, 2001-01-02]}')) + = asText(ts2cell '{[47c3c3@2001-01-01, 47c3c3@2001-01-02]}'); +---- +true diff --git a/tools/codegen_duck_udfs.py b/tools/codegen_duck_udfs.py index a0ee312a..6ceefb17 100644 --- a/tools/codegen_duck_udfs.py +++ b/tools/codegen_duck_udfs.py @@ -5366,6 +5366,7 @@ def emit_container_scalar(f, ops, rev): # set, a span or a span set, and the sequence constructors over an array "meos_temporal_constructor", "meos_geo_constructor", "meos_json_constructor", "meos_pointcloud_constructor", + "meos_h3_constructor", "meos_quadbin_constructor", "meos_s2cell_constructor", # The functions of the temporal JSON values, each the computation of one # operation (tjsonbObjectField, tjsonbPathExists and tjsonbPathExistsTz over one C # function binding its tz), called as the catalog places their arguments @@ -6421,6 +6422,7 @@ def sig_call_arg(f, p, t, k, arrays, lenp): # or the SQL type the pointer's own DuckDB type is named after (jsonpath for JsonPath, # TJsonbTypes::jsonpath()), a type no base, span or set map carries acc = (BASE_LOGICAL.get(t) or SQL_BASE_TO_DUCK.get(t) or SET_SIG_ACC.get(t) + or SPANSET_TYPES.get(t) or (PTR_IN[b][0] if PTR_IN[b][0].endswith("::%s()" % t) else None)) if not acc or (b in BASEVAL_PTR_IN and PTR_IN[b][0] != acc): return None