diff --git a/src/generated/generated_temporal_udfs_2.cpp b/src/generated/generated_temporal_udfs_2.cpp index a4a4c5e6..fe35a712 100644 --- a/src/generated/generated_temporal_udfs_2.cpp +++ b/src/generated/generated_temporal_udfs_2.cpp @@ -2162,69 +2162,75 @@ static void Gen_tdistance_tnumber_tnumber(DataChunk &args, ExpressionState &, Ve static void Gen_nad_tfloat_float(DataChunk &args, ExpressionState &, Vector &result) { EnsureMeosThreadInitialized(); - BinaryExecutor::Execute(args.data[0], args.data[1], result, args.size(), - [&](string_t in, double a2) { + BinaryExecutor::ExecuteWithNulls(args.data[0], args.data[1], result, args.size(), + [&](string_t in, double a2, ValidityMask &mask, idx_t idx) -> double { Temporal *t = BlobToTemporal(in); double r = nad_tfloat_float(t, a2); free(t); + if (r == std::numeric_limits::max()) { mask.SetInvalid(idx); return double(); } return (double) r; }); } static void Gen_nad_tfloat_tfloat(DataChunk &args, ExpressionState &, Vector &result) { EnsureMeosThreadInitialized(); - BinaryExecutor::Execute(args.data[0], args.data[1], result, args.size(), - [&](string_t in1, string_t in2) { + BinaryExecutor::ExecuteWithNulls(args.data[0], args.data[1], result, args.size(), + [&](string_t in1, string_t in2, ValidityMask &mask, idx_t idx) -> double { Temporal *t1 = BlobToTemporal(in1); Temporal *t2 = BlobToTemporal(in2); double r = nad_tfloat_tfloat(t1, t2); free(t1); free(t2); + if (r == std::numeric_limits::max()) { mask.SetInvalid(idx); return double(); } return (double) r; }); } static void Gen_nad_tbigint_bigint(DataChunk &args, ExpressionState &, Vector &result) { EnsureMeosThreadInitialized(); - BinaryExecutor::Execute(args.data[0], args.data[1], result, args.size(), - [&](string_t in, int64_t a2) { + BinaryExecutor::ExecuteWithNulls(args.data[0], args.data[1], result, args.size(), + [&](string_t in, int64_t a2, ValidityMask &mask, idx_t idx) -> double { Temporal *t = BlobToTemporal(in); int64_t r = nad_tbigint_bigint(t, a2); free(t); + if (r == std::numeric_limits::max()) { mask.SetInvalid(idx); return double(); } return (double) r; }); } static void Gen_nad_tbigint_tbigint(DataChunk &args, ExpressionState &, Vector &result) { EnsureMeosThreadInitialized(); - BinaryExecutor::Execute(args.data[0], args.data[1], result, args.size(), - [&](string_t in1, string_t in2) { + BinaryExecutor::ExecuteWithNulls(args.data[0], args.data[1], result, args.size(), + [&](string_t in1, string_t in2, ValidityMask &mask, idx_t idx) -> double { Temporal *t1 = BlobToTemporal(in1); Temporal *t2 = BlobToTemporal(in2); int64_t r = nad_tbigint_tbigint(t1, t2); free(t1); free(t2); + if (r == std::numeric_limits::max()) { mask.SetInvalid(idx); return double(); } return (double) r; }); } static void Gen_nad_tint_int(DataChunk &args, ExpressionState &, Vector &result) { EnsureMeosThreadInitialized(); - BinaryExecutor::Execute(args.data[0], args.data[1], result, args.size(), - [&](string_t in, int32_t a2) { + BinaryExecutor::ExecuteWithNulls(args.data[0], args.data[1], result, args.size(), + [&](string_t in, int32_t a2, ValidityMask &mask, idx_t idx) -> double { Temporal *t = BlobToTemporal(in); int32_t r = nad_tint_int(t, a2); free(t); + if (r == std::numeric_limits::max()) { mask.SetInvalid(idx); return double(); } return (double) r; }); } static void Gen_nad_tint_tint(DataChunk &args, ExpressionState &, Vector &result) { EnsureMeosThreadInitialized(); - BinaryExecutor::Execute(args.data[0], args.data[1], result, args.size(), - [&](string_t in1, string_t in2) { + BinaryExecutor::ExecuteWithNulls(args.data[0], args.data[1], result, args.size(), + [&](string_t in1, string_t in2, ValidityMask &mask, idx_t idx) -> double { Temporal *t1 = BlobToTemporal(in1); Temporal *t2 = BlobToTemporal(in2); int32_t r = nad_tint_tint(t1, t2); free(t1); free(t2); + if (r == std::numeric_limits::max()) { mask.SetInvalid(idx); return double(); } return (double) r; }); } diff --git a/test/sql/parity/036_tnumber_distance.test b/test/sql/parity/036_tnumber_distance.test index 2caae083..30d71419 100644 --- a/test/sql/parity/036_tnumber_distance.test +++ b/test/sql/parity/036_tnumber_distance.test @@ -59,3 +59,21 @@ query I SELECT typeof("<->"('1@2000-01-01'::tint, '2@2000-01-01'::tint)); ---- tint + +# ── two values sharing no time have no nearest approach ─────────────────────── +# NAD_tnumber_tnumber returns NULL where nad_tnumber_tnumber answers the distance +# sentinel, the maximum of the type the distance is computed in. +query I +SELECT nearestApproachDistance(tint '[1@2000-01-01, 5@2000-01-05]', tint '[3@2000-02-01, 3@2000-02-05]') IS NULL; +---- +true + +query I +SELECT nearestApproachDistance(tbigint '[1@2000-01-01, 5@2000-01-05]', tbigint '[3@2000-02-01, 3@2000-02-05]') IS NULL; +---- +true + +query I +SELECT nearestApproachDistance(tfloat '[1@2000-01-01, 5@2000-01-05]', tfloat '[3@2000-02-01, 3@2000-02-05]') IS NULL; +---- +true diff --git a/tools/codegen_duck_udfs.py b/tools/codegen_duck_udfs.py index 0c0a0972..e071e73a 100644 --- a/tools/codegen_duck_udfs.py +++ b/tools/codegen_duck_udfs.py @@ -481,9 +481,13 @@ def scalar_emit3(f): # (`shape.nullableResult`, read from the wrapper's `if () PG_RETURN_NULL();`): a # three-valued predicate answering unknown, and the distance sentinel. They name the result # `result`; the generated body names it `r`. A pointer result's guard (`! result`, -# `result == NULL`) is the NULL pointer the pointer paths already map. +# `result == NULL`) is the NULL pointer the pointer paths already map. The distance sentinel of +# distance_sentinel is the maximum of the type MEOS returns, tested on `r` in that type before +# its conversion to the declared one, as #emit_temporal_box tests it. SCALAR_NULL_GUARD = {"if (result < 0)": "r < 0", - "if (result == DBL_MAX)": "r == DBL_MAX"} + "if (result == DBL_MAX)": "r == DBL_MAX", + "if (datum_eq(result, distance_sentinel(basetype), basetype))": + "r == std::numeric_limits::max()"} def scalar_exec(f, rett, rexpr): """(executor method, extra lambda parameters, lambda return annotation, return statement) for