Skip to content

Commit fe60666

Browse files
feat(io): complete binary + hex-WKB round-trip I/O for all types
Adds asBinary / xFromBinary and asHexWKB / xFromHexWKB for the types that were missing them, mirroring the roundtrip surface that tgeompoint and tgeogpoint already had: - Span (intspan / bigintspan / floatspan / datespan / tstzspan). - Spanset (intspanset / bigintspanset / floatspanset / datespanset / tstzspanset). - Set (intset / bigintset / floatset / textset / dateset / tstzset); asBinary already existed, fromBinary added. - Tgeometry: asBinary / tgeometryFromBinary via temporal_as_wkb / temporal_from_wkb (subtype-agnostic MEOS exports). The new wkb_roundtrip.test exercises every pair as a round-trip equality check against the original input — assertions are timezone-neutral (= comparisons or asText round-trips), so they hold regardless of the process timezone (CI uses TZ=UTC, developer machines may use anything).
1 parent 4cc9a9c commit fe60666

15 files changed

Lines changed: 768 additions & 33 deletions

src/geo/tgeometry_in_out.cpp

Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -195,22 +195,98 @@ bool TgeometryFunctions::TgeometryToString(Vector &source, Vector &result, idx_t
195195
return true;
196196
}
197197

198+
namespace {
199+
200+
void TgeometryAsWkbExec(DataChunk &args, ExpressionState &, Vector &result) {
201+
UnaryExecutor::Execute<string_t, string_t>(
202+
args.data[0], result, args.size(),
203+
[&](string_t input) -> string_t {
204+
size_t sz = input.GetSize();
205+
uint8_t *copy = (uint8_t *)malloc(sz);
206+
if (!copy) throw InternalException("asBinary: malloc failed");
207+
memcpy(copy, input.GetData(), sz);
208+
Temporal *t = reinterpret_cast<Temporal *>(copy);
209+
size_t wkb_sz = 0;
210+
uint8_t *wkb = temporal_as_wkb(t, WKB_EXTENDED, &wkb_sz);
211+
free(copy);
212+
if (!wkb || wkb_sz == 0) { if (wkb) free(wkb); throw InternalException("temporal_as_wkb failed"); }
213+
string_t stored = StringVector::AddStringOrBlob(
214+
result, string_t(reinterpret_cast<const char *>(wkb), wkb_sz));
215+
free(wkb);
216+
return stored;
217+
});
218+
}
219+
220+
void TgeometryFromWkbExec(DataChunk &args, ExpressionState &, Vector &result) {
221+
UnaryExecutor::Execute<string_t, string_t>(
222+
args.data[0], result, args.size(),
223+
[&](string_t input) -> string_t {
224+
if (input.GetSize() == 0)
225+
throw InvalidInputException("fromBinary: empty WKB input");
226+
uint8_t *wkb = (uint8_t *)malloc(input.GetSize());
227+
if (!wkb) throw InternalException("fromBinary: malloc failed");
228+
memcpy(wkb, input.GetData(), input.GetSize());
229+
Temporal *t = temporal_from_wkb(wkb, input.GetSize());
230+
free(wkb);
231+
if (!t) throw InvalidInputException("fromBinary: invalid tgeometry WKB");
232+
size_t sz = temporal_mem_size(t);
233+
string_t stored = StringVector::AddStringOrBlob(
234+
result, string_t(reinterpret_cast<const char *>(t), sz));
235+
free(t);
236+
return stored;
237+
});
238+
}
239+
240+
void TgeometryAsHexWkbExec(DataChunk &args, ExpressionState &, Vector &result) {
241+
UnaryExecutor::Execute<string_t, string_t>(
242+
args.data[0], result, args.size(),
243+
[&](string_t input) -> string_t {
244+
size_t sz = input.GetSize();
245+
uint8_t *copy = (uint8_t *)malloc(sz);
246+
if (!copy) throw InternalException("asHexWKB: malloc failed");
247+
memcpy(copy, input.GetData(), sz);
248+
Temporal *t = reinterpret_cast<Temporal *>(copy);
249+
size_t hex_sz = 0;
250+
char *hex = temporal_as_hexwkb(t, WKB_EXTENDED, &hex_sz);
251+
free(copy);
252+
if (!hex || hex_sz == 0) { if (hex) free(hex); throw InternalException("temporal_as_hexwkb failed"); }
253+
string_t stored = StringVector::AddString(result, hex, hex_sz);
254+
free(hex);
255+
return stored;
256+
});
257+
}
258+
259+
void TgeometryFromHexWkbExec(DataChunk &args, ExpressionState &, Vector &result) {
260+
UnaryExecutor::Execute<string_t, string_t>(
261+
args.data[0], result, args.size(),
262+
[&](string_t input) -> string_t {
263+
if (input.GetSize() == 0)
264+
throw InvalidInputException("fromHexWKB: empty hex input");
265+
std::string hex_str(input.GetData(), input.GetSize());
266+
Temporal *t = temporal_from_hexwkb(hex_str.c_str());
267+
if (!t) throw InvalidInputException("fromHexWKB: invalid tgeometry hex WKB");
268+
size_t sz = temporal_mem_size(t);
269+
string_t stored = StringVector::AddStringOrBlob(
270+
result, string_t(reinterpret_cast<const char *>(t), sz));
271+
free(t);
272+
return stored;
273+
});
274+
}
275+
276+
} // anonymous namespace
277+
198278
void TGeometryTypes::RegisterScalarInOutFunctions(ExtensionLoader &loader){
199-
auto TgeometryAsText = ScalarFunction(
200-
"asText",
201-
{TGeometryTypes::TGEOMETRY()},
202-
LogicalType::VARCHAR,
203-
Tspatial_as_text
204-
);
205-
duckdb::RegisterSerializedScalarFunction(loader, TgeometryAsText);
206-
207-
auto TgeometryAsEWKT = ScalarFunction(
208-
"asEWKT",
209-
{TGeometryTypes::TGEOMETRY()},
210-
LogicalType::VARCHAR,
211-
Tspatial_as_ewkt
212-
);
213-
duckdb::RegisterSerializedScalarFunction(loader, TgeometryAsEWKT);
279+
const auto T = TGeometryTypes::TGEOMETRY();
280+
const auto B = LogicalType::BLOB;
281+
const auto V = LogicalType::VARCHAR;
282+
283+
duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("asBinary", {T}, B, TgeometryAsWkbExec));
284+
duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("tgeometryFromBinary", {B}, T, TgeometryFromWkbExec));
285+
duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("asHexWKB", {T}, V, TgeometryAsHexWkbExec));
286+
duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("tgeometryFromHexWKB", {V}, T, TgeometryFromHexWkbExec));
287+
288+
duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("asText", {T}, V, Tspatial_as_text));
289+
duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("asEWKT", {T}, V, Tspatial_as_ewkt));
214290
}
215291

216292

src/geo/tgeompoint.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,7 +1701,7 @@ void TgeompointType::RegisterScalarFunctions(ExtensionLoader &loader) {
17011701
)
17021702
);
17031703

1704-
duckdb::RegisterSerializedScalarFunction(loader,
1704+
duckdb::RegisterSerializedScalarFunction(loader,
17051705
ScalarFunction(
17061706
"&&", // overlaps
17071707
{TGEOMPOINT(), SpanTypes::TSTZSPAN()},
@@ -1710,7 +1710,16 @@ void TgeompointType::RegisterScalarFunctions(ExtensionLoader &loader) {
17101710
)
17111711
);
17121712

1713-
duckdb::RegisterSerializedScalarFunction(loader,
1713+
duckdb::RegisterSerializedScalarFunction(loader,
1714+
ScalarFunction(
1715+
"&&", // overlaps (stbox-level bounding-box overlap between two tgeompoints)
1716+
{TGEOMPOINT(), TGEOMPOINT()},
1717+
LogicalType::BOOLEAN,
1718+
TgeompointFunctions::Temporal_overlaps_tgeompoint_tgeompoint
1719+
)
1720+
);
1721+
1722+
duckdb::RegisterSerializedScalarFunction(loader,
17141723
ScalarFunction(
17151724
"@>", // contains
17161725
{TGEOMPOINT(), StboxType::STBOX()},

src/geo/tgeompoint_functions.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3094,6 +3094,29 @@ void TgeompointFunctions::Temporal_overlaps_tgeompoint_tstzspan(DataChunk &args,
30943094
}
30953095
}
30963096

3097+
void TgeompointFunctions::Temporal_overlaps_tgeompoint_tgeompoint(DataChunk &args, ExpressionState &state, Vector &result) {
3098+
BinaryExecutor::Execute<string_t, string_t, bool>(
3099+
args.data[0], args.data[1], result, args.size(),
3100+
[&](string_t t1_blob, string_t t2_blob) -> bool {
3101+
uint8_t *t1_copy = (uint8_t*)malloc(t1_blob.GetSize());
3102+
memcpy(t1_copy, t1_blob.GetData(), t1_blob.GetSize());
3103+
Temporal *t1 = reinterpret_cast<Temporal*>(t1_copy);
3104+
3105+
uint8_t *t2_copy = (uint8_t*)malloc(t2_blob.GetSize());
3106+
memcpy(t2_copy, t2_blob.GetData(), t2_blob.GetSize());
3107+
Temporal *t2 = reinterpret_cast<Temporal*>(t2_copy);
3108+
3109+
bool ret = overlaps_tspatial_tspatial(t1, t2);
3110+
free(t1);
3111+
free(t2);
3112+
return ret;
3113+
}
3114+
);
3115+
if (args.size() == 1) {
3116+
result.SetVectorType(VectorType::CONSTANT_VECTOR);
3117+
}
3118+
}
3119+
30973120
void TgeompointFunctions::Temporal_contains_tgeompoint_stbox(DataChunk &args, ExpressionState &state, Vector &result) {
30983121
BinaryExecutor::Execute<string_t, string_t, bool>(
30993122
args.data[0], args.data[1], result, args.size(),

src/include/geo/tgeompoint_functions.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ struct TgeompointFunctions {
152152
****************************************************/
153153
static void Temporal_overlaps_tgeompoint_stbox(DataChunk &args, ExpressionState &state, Vector &result);
154154
static void Temporal_overlaps_tgeompoint_tstzspan(DataChunk &args, ExpressionState &state, Vector &result);
155+
static void Temporal_overlaps_tgeompoint_tgeompoint(DataChunk &args, ExpressionState &state, Vector &result);
155156
static void Temporal_contains_tgeompoint_stbox(DataChunk &args, ExpressionState &state, Vector &result);
156157

157158
/* ***************************************************

src/include/temporal/set_functions.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ struct SetFunctions {
2929
// scalar functions
3030
static void Set_as_text(DataChunk &args, ExpressionState &state, Vector &result);
3131
static void Set_as_binary(DataChunk &args, ExpressionState &state, Vector &result);
32+
static void Set_from_binary(DataChunk &args, ExpressionState &state, Vector &result);
3233
static void Set_as_hexwkb(DataChunk &args, ExpressionState &state, Vector &result);
34+
static void Set_from_hexwkb(DataChunk &args, ExpressionState &state, Vector &result);
3335
static void Set_constructor(DataChunk &args, ExpressionState &state, Vector &result);
3436
static void Value_to_set(DataChunk &args, ExpressionState &state, Vector &result);
3537
static void Intset_to_floatset(DataChunk &args, ExpressionState &state, Vector &result);

src/include/temporal/span_functions.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ struct SpanFunctions {
2828
// TODO (Type Range): static bool Range_to_span_cast(Vector &source, Vector &result, idx_t count, CastParameters &parameters);
2929
// TODO (Type Range): static bool Span_to_range_cast(Vector &source, Vector &result, idx_t count, CastParameters &parameters);
3030
// scalar functions
31+
static void Span_as_binary(DataChunk &args, ExpressionState &state, Vector &result);
32+
static void Span_from_binary(DataChunk &args, ExpressionState &state, Vector &result);
33+
static void Span_as_hexwkb(DataChunk &args, ExpressionState &state, Vector &result);
34+
static void Span_from_hexwkb(DataChunk &args, ExpressionState &state, Vector &result);
3135
static void Span_as_text(DataChunk &args, ExpressionState &state, Vector &result);
3236
static void Span_constructor(DataChunk &args, ExpressionState &state, Vector &result);
3337
static void Span_binary_constructor(DataChunk &args, ExpressionState &state, Vector &result);

src/include/temporal/spanset_functions.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ struct SpansetFunctions{
2727
static bool Datespanset_to_tstzspanset_cast(Vector &source, Vector &result, idx_t count, CastParameters &parameters);
2828
static bool Tstzspanset_to_datespanset_cast(Vector &source, Vector &result, idx_t count, CastParameters &parameters);
2929

30-
// other
30+
// other
31+
static void Spanset_as_binary(DataChunk &args, ExpressionState &state, Vector &result);
32+
static void Spanset_from_binary(DataChunk &args, ExpressionState &state, Vector &result);
33+
static void Spanset_as_hexwkb(DataChunk &args, ExpressionState &state, Vector &result);
34+
static void Spanset_from_hexwkb(DataChunk &args, ExpressionState &state, Vector &result);
3135
static void Spanset_as_text(DataChunk &args, ExpressionState &state, Vector &result);
3236

3337
static void Spanset_constructor(DataChunk &args, ExpressionState &state, Vector &result);

src/temporal/set.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,13 +314,22 @@ void SetTypes::RegisterScalarFunctions(ExtensionLoader &loader) {
314314
ScalarFunction("+", {set_type, set_type}, set_type, SetFunctions::Union_set_set)
315315
);
316316

317-
duckdb::RegisterSerializedScalarFunction(loader,
317+
duckdb::RegisterSerializedScalarFunction(loader,
318318
ScalarFunction("asBinary", {set_type}, LogicalType::BLOB, SetFunctions::Set_as_binary)
319319
);
320320

321-
duckdb::RegisterSerializedScalarFunction(loader,
321+
duckdb::RegisterSerializedScalarFunction(loader,
322+
ScalarFunction(set_type.ToString() + "FromBinary",
323+
{LogicalType::BLOB}, set_type, SetFunctions::Set_from_binary)
324+
);
325+
326+
duckdb::RegisterSerializedScalarFunction(loader,
322327
ScalarFunction("asHexWKB", {set_type}, LogicalType::VARCHAR, SetFunctions::Set_as_hexwkb)
323328
);
329+
duckdb::RegisterSerializedScalarFunction(loader,
330+
ScalarFunction(set_type.ToString() + "FromHexWKB",
331+
{LogicalType::VARCHAR}, set_type, SetFunctions::Set_from_hexwkb)
332+
);
324333
}
325334

326335
duckdb::RegisterSerializedScalarFunction(loader,

src/temporal/set_functions.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,26 @@ void SetFunctions::Set_as_binary(DataChunk &args, ExpressionState &state, Vector
106106
);
107107
}
108108

109+
// --- fromBinary ---
110+
void SetFunctions::Set_from_binary(DataChunk &args, ExpressionState &state, Vector &result) {
111+
UnaryExecutor::Execute<string_t, string_t>(
112+
args.data[0], result, args.size(),
113+
[&](string_t input) -> string_t {
114+
if (input.GetSize() == 0)
115+
throw InvalidInputException("fromBinary: empty WKB input");
116+
uint8_t *wkb = (uint8_t *)malloc(input.GetSize());
117+
if (!wkb) throw InternalException("fromBinary: malloc failed");
118+
memcpy(wkb, input.GetData(), input.GetSize());
119+
Set *s = set_from_wkb(wkb, input.GetSize());
120+
free(wkb);
121+
if (!s) throw InvalidInputException("fromBinary: invalid set WKB");
122+
string_t stored = StringVector::AddStringOrBlob(
123+
result, string_t(reinterpret_cast<const char *>(s), set_mem_size(s)));
124+
free(s);
125+
return stored;
126+
});
127+
}
128+
109129
// --- asHexWKB ---
110130
void SetFunctions::Set_as_hexwkb(DataChunk &args, ExpressionState &state, Vector &result) {
111131
UnaryExecutor::Execute<string_t, string_t>(
@@ -134,6 +154,24 @@ void SetFunctions::Set_as_hexwkb(DataChunk &args, ExpressionState &state, Vector
134154
}
135155

136156

157+
// --- fromHexWKB ---
158+
void SetFunctions::Set_from_hexwkb(DataChunk &args, ExpressionState &, Vector &result) {
159+
UnaryExecutor::Execute<string_t, string_t>(
160+
args.data[0], result, args.size(),
161+
[&](string_t input) -> string_t {
162+
if (input.GetSize() == 0)
163+
throw InvalidInputException("fromHexWKB: empty hex input");
164+
std::string hex_str(input.GetData(), input.GetSize());
165+
Set *s = set_from_hexwkb(hex_str.c_str());
166+
if (!s) throw InvalidInputException("fromHexWKB: invalid set hex WKB");
167+
string_t stored = StringVector::AddStringOrBlob(
168+
result, string_t(reinterpret_cast<const char *>(s), set_mem_size(s)));
169+
free(s);
170+
return stored;
171+
});
172+
}
173+
174+
137175
// --- Cast From String ---
138176
bool SetFunctions::Set_to_text(Vector &source, Vector &result, idx_t count, CastParameters &parameters) {
139177
source.Flatten(count);

src/temporal/span.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "time_util.hpp"
1414

15+
#include <algorithm>
1516
#include <regex>
1617
#include <string>
1718
#include "mobilityduck/meos_exec_serial.hpp"
@@ -154,9 +155,23 @@ void SpanTypes::RegisterCastFunctions(ExtensionLoader &loader) {
154155
}
155156
}
156157

157-
void SpanTypes::RegisterScalarFunctions(ExtensionLoader &loader) {
158+
void SpanTypes::RegisterScalarFunctions(ExtensionLoader &loader) {
158159
for (const auto &span_type : SpanTypes::AllTypes()) {
159-
auto base_type = SpanTypeMapping::GetChildType(span_type);
160+
auto base_type = SpanTypeMapping::GetChildType(span_type);
161+
std::string alias = span_type.ToString();
162+
std::string lower_alias = alias;
163+
std::transform(lower_alias.begin(), lower_alias.end(), lower_alias.begin(), ::tolower);
164+
165+
duckdb::RegisterSerializedScalarFunction(loader,
166+
ScalarFunction("asBinary", {span_type}, LogicalType::BLOB, SpanFunctions::Span_as_binary));
167+
duckdb::RegisterSerializedScalarFunction(loader,
168+
ScalarFunction(lower_alias + "FromBinary",
169+
{LogicalType::BLOB}, span_type, SpanFunctions::Span_from_binary));
170+
duckdb::RegisterSerializedScalarFunction(loader,
171+
ScalarFunction("asHexWKB", {span_type}, LogicalType::VARCHAR, SpanFunctions::Span_as_hexwkb));
172+
duckdb::RegisterSerializedScalarFunction(loader,
173+
ScalarFunction(lower_alias + "FromHexWKB",
174+
{LogicalType::VARCHAR}, span_type, SpanFunctions::Span_from_hexwkb));
160175

161176
// Register: asText
162177
if (span_type == SpanTypes::FLOATSPAN()) {

0 commit comments

Comments
 (0)