diff --git a/cpp/src/gandiva/gdv_function_stubs_test.cc b/cpp/src/gandiva/gdv_function_stubs_test.cc index c0197eb815c8..d5b125a0ff23 100644 --- a/cpp/src/gandiva/gdv_function_stubs_test.cc +++ b/cpp/src/gandiva/gdv_function_stubs_test.cc @@ -404,6 +404,31 @@ TEST(TestGdvFnStubs, TestCastVARCHARFromMilliseconds) { ctx.Reset(); } +TEST(TestGdvFnStubs, TestCastVARCHARFromMillisecondsOutOfRange) { + gandiva::ExecutionContext ctx; + int64_t ctx_ptr = reinterpret_cast(&ctx); + int32_t len_a = 0, len_b = 0; + + // date64 is in milliseconds; this value is far past the year range the + // formatter can print, so StringFormatter emits the longer + // "" rendering, which is well over 10 bytes. + gdv_date64 out_of_range = 9000000000000000LL; + const char* a = gdv_fn_castVARCHAR_date64_int64(ctx_ptr, out_of_range, 100, &len_a); + std::string expected_a(a, len_a); + EXPECT_EQ(expected_a, ""); + EXPECT_FALSE(ctx.has_error()); + + // The next cast reuses the same arena chunk immediately after `a`. If `a` only + // reserved 10 bytes the longer write ran past its slot, so this allocation + // overlaps and corrupts it; with the buffer sized for the real output the two + // do not overlap and `a` is still intact here. + gdv_date64 ts = StringToTimestamp("2021-04-23 10:20:33"); + gdv_fn_castVARCHAR_date64_int64(ctx_ptr, ts, 100, &len_b); + EXPECT_FALSE(ctx.has_error()); + EXPECT_EQ(std::string(a, len_a), expected_a); + ctx.Reset(); +} + TEST(TestGdvFnStubs, TestCastVARCHARFromFloat) { gandiva::ExecutionContext ctx; uint64_t ctx_ptr = reinterpret_cast(&ctx); diff --git a/cpp/src/gandiva/gdv_string_function_stubs.cc b/cpp/src/gandiva/gdv_string_function_stubs.cc index 55f5f13ac312..0c45ff24e6c7 100644 --- a/cpp/src/gandiva/gdv_string_function_stubs.cc +++ b/cpp/src/gandiva/gdv_string_function_stubs.cc @@ -160,10 +160,14 @@ const char* gdv_fn_regexp_extract_utf8_utf8_int32(int64_t ptr, int64_t holder_pt return ret; \ } -// Macro for date64 type. Output is always "YYYY-MM-DD" = 10 chars max. +// Macro for date64 type. StringFormatter emits "YYYY-MM-DD" for +// ordinary dates, but widens to "-YYYYY-MM-DD" for years outside four digits and +// to ">" for millisecond values beyond its supported +// year range, so the rendering can be well over 10 bytes. Size the buffer for the +// longest one: "" (1). #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; \ + constexpr int32_t max_date_str_len = 42; \ int32_t alloc_len = \ static_cast(len < max_date_str_len ? len : max_date_str_len); \ GDV_FN_CAST_VARLEN_ALLOC(alloc_len) \