Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions cpp/src/gandiva/gdv_function_stubs_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,35 @@ TEST(TestGdvFnStubs, TestCastVARCHARFromMilliseconds) {
ctx.Reset();
}

// Dates whose formatted form is longer than "YYYY-MM-DD" must still fit in the
// block they are written to. A following allocation from the same arena would
// otherwise land inside the bytes this cast already wrote.
TEST(TestGdvFnStubs, TestCastVARCHARFromMillisecondsLongOutput) {
gandiva::ExecutionContext ctx;
uint64_t ctx_ptr = reinterpret_cast<int64_t>(&ctx);
int32_t out_len = 0;
int32_t other_len = 0;

// Year 10000 needs 11 bytes.
const char* out_str =
gdv_fn_castVARCHAR_date64_int64(ctx_ptr, 253402300800000LL, 100, &out_len);
gdv_fn_castVARCHAR_int32_int64(ctx_ptr, 12345, 100, &other_len);
EXPECT_EQ(std::string(out_str, out_len), "10000-01-01");
EXPECT_FALSE(ctx.has_error());

// Out-of-range dates format as a diagnostic string, far longer than a date.
out_str = gdv_fn_castVARCHAR_date64_int64(ctx_ptr, std::numeric_limits<int64_t>::min(),
100, &out_len);
gdv_fn_castVARCHAR_int32_int64(ctx_ptr, 12345, 100, &other_len);
EXPECT_EQ(std::string(out_str, out_len), "<value out of range: -9223372036854775808>");
EXPECT_FALSE(ctx.has_error());

// Truncation to the requested length still applies.
out_str = gdv_fn_castVARCHAR_date64_int64(ctx_ptr, 253402300800000LL, 5, &out_len);
EXPECT_EQ(std::string(out_str, out_len), "10000");
EXPECT_FALSE(ctx.has_error());
}

TEST(TestGdvFnStubs, TestCastVARCHARFromFloat) {
gandiva::ExecutionContext ctx;
uint64_t ctx_ptr = reinterpret_cast<int64_t>(&ctx);
Expand Down
61 changes: 31 additions & 30 deletions cpp/src/gandiva/gdv_string_function_stubs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -132,41 +132,42 @@ const char* gdv_fn_regexp_extract_utf8_utf8_int32(int64_t ptr, int64_t holder_pt

// Helper: invoke formatter callback, copy result to ret, handle errors.
// Used by date64 and float types that rely on arrow::internal::StringFormatter.
#define GDV_FN_CAST_VARLEN_FORMATTER_SUFFIX \
arrow::Status status = formatter(value, [&](std::string_view v) { \
int64_t size = static_cast<int64_t>(v.size()); \
*out_len = static_cast<int32_t>(len < size ? len : size); \
memcpy(ret, v.data(), *out_len); \
return arrow::Status::OK(); \
}); \
if (!status.ok()) { \
std::string err = "Could not cast " + std::to_string(value) + " to string"; \
gdv_fn_context_set_error_msg(context, err.c_str()); \
*out_len = 0; \
return ""; \
} \
return ret; \
// The formatted length is only known inside the callback, so the arena block is
// allocated there: sizing it from a fixed upper bound guessed at the call site
// lets the copy below run past the end whenever the formatter emits more than
// that bound.
#define GDV_FN_CAST_VARLEN_FORMATTER_SUFFIX \
char* ret = nullptr; \
arrow::Status status = formatter(value, [&](std::string_view v) { \
int64_t size = static_cast<int64_t>(v.size()); \
*out_len = static_cast<int32_t>(len < size ? len : size); \
ret = reinterpret_cast<char*>(gdv_fn_context_arena_malloc(context, *out_len)); \
if (ret == nullptr) { \
return arrow::Status::OutOfMemory("Could not allocate memory"); \
} \
memcpy(ret, v.data(), *out_len); \
return arrow::Status::OK(); \
}); \
if (!status.ok() || ret == nullptr) { \
std::string err = "Could not cast " + std::to_string(value) + " to string"; \
gdv_fn_context_set_error_msg(context, err.c_str()); \
*out_len = 0; \
return ""; \
} \
return ret; \
}

// Macro for date64 type. Output is always "YYYY-MM-DD" = 10 chars max.
#define GDV_FN_CAST_VARLEN_TYPE_FROM_DATE64(IN_TYPE, CAST_NAME, ARROW_TYPE) \
GDV_FN_CAST_VARLEN_PREFIX(IN_TYPE, CAST_NAME) \
constexpr int32_t max_date_str_len = 10; \
int32_t alloc_len = \
static_cast<int32_t>(len < max_date_str_len ? len : max_date_str_len); \
GDV_FN_CAST_VARLEN_ALLOC(alloc_len) \
arrow::internal::StringFormatter<arrow::ARROW_TYPE> formatter; \
// Macro for date64 type. Out-of-range values format as a diagnostic string that
// is much longer than "YYYY-MM-DD", so the output size comes from the formatter.
#define GDV_FN_CAST_VARLEN_TYPE_FROM_DATE64(IN_TYPE, CAST_NAME, ARROW_TYPE) \
GDV_FN_CAST_VARLEN_PREFIX(IN_TYPE, CAST_NAME) \
arrow::internal::StringFormatter<arrow::ARROW_TYPE> formatter; \
GDV_FN_CAST_VARLEN_FORMATTER_SUFFIX

// Macro for float types (float32/float64). Uses Java-compatible formatting.
// Max string: "-1.2345678901234567E-308" = 24 chars.
#define GDV_FN_CAST_VARLEN_TYPE_FROM_REAL(IN_TYPE, CAST_NAME, ARROW_TYPE) \
GDV_FN_CAST_VARLEN_PREFIX(IN_TYPE, CAST_NAME) \
constexpr int32_t max_real_str_len = 24; \
int32_t alloc_len = \
static_cast<int32_t>(len < max_real_str_len ? len : max_real_str_len); \
GDV_FN_CAST_VARLEN_ALLOC(alloc_len) \
gandiva::GdvStringFormatter<arrow::ARROW_TYPE> formatter; \
#define GDV_FN_CAST_VARLEN_TYPE_FROM_REAL(IN_TYPE, CAST_NAME, ARROW_TYPE) \
GDV_FN_CAST_VARLEN_PREFIX(IN_TYPE, CAST_NAME) \
gandiva::GdvStringFormatter<arrow::ARROW_TYPE> formatter; \
GDV_FN_CAST_VARLEN_FORMATTER_SUFFIX

// Use optimized integer macro for int32/int64, date64 macro, and real macro for floats
Expand Down
Loading