From cf50338e081b7d6177092c0d4ed00ecc17a49fd2 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 23 Jul 2026 15:45:58 +0200 Subject: [PATCH] Generate the TNumber base-value accessors The value-at-n and value-at-timestamp accessors of tint and tfloat return the base value through a boolean-plus-result out-parameter, which the converter covered only for timestamps. Extend the bool-plus-result converter to the base scalar out-parameters, so valueN and valueAtTimestamptz fold to the boxed base value (int or double), or null when the boolean reports no value. The converter reads the concrete element type from the catalog, so no per-class configuration is needed. A parity test checks valueN and valueAtTimestamptz against direct GeneratedFunctions calls. --- .../src/main/java/ObjectLayerGenerator.java | 23 +++++++++++++++---- .../GeneratedConcreteNumberParityTest.java | 16 +++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/codegen/src/main/java/ObjectLayerGenerator.java b/codegen/src/main/java/ObjectLayerGenerator.java index fbbe3f129..62788badf 100644 --- a/codegen/src/main/java/ObjectLayerGenerator.java +++ b/codegen/src/main/java/ObjectLayerGenerator.java @@ -502,13 +502,28 @@ && cleanType(fn.path("returnType").path("c").asText()).equals("Temporal *")) { String returnKind; String returnSubtype = temporalReturn(retC); if (resultOut != null) { - if (!paramCType(params, resultOut).equals("TimestampTz *")) { - defer(ooName, "bool+result of " + paramCType(params, resultOut) + " needs a converter"); + String outT = paramCType(params, resultOut); + // A boxed return type carries the null the boolean-false case folds to; the reader pulls the + // value out of the single-element buffer. + returnType = switch (outT) { + case "TimestampTz *" -> "java.time.OffsetDateTime"; + case "int *", "int32 *", "int32_t *" -> "Integer"; + case "double *", "float8 *" -> "Double"; + case "int64 *", "int64_t *" -> "Long"; + default -> null; + }; + returnSubtype = switch (outT) { + case "TimestampTz *" -> "utils.TimestampTzConverter.toOffsetDateTime(_r.getLong(0))"; + case "int *", "int32 *", "int32_t *" -> "_r.getInt(0)"; + case "double *", "float8 *" -> "_r.getDouble(0)"; + case "int64 *", "int64_t *" -> "_r.getLong(0)"; + default -> null; + }; + if (returnType == null) { + defer(ooName, "bool+result of " + outT + " needs a converter"); return null; } - returnType = "java.time.OffsetDateTime"; returnKind = "boolResult"; - returnSubtype = "utils.TimestampTzConverter.toOffsetDateTime(_r.getLong(0))"; } else if (returnSubtype != null) { returnType = "Temporal"; returnKind = "temporal"; diff --git a/jmeos-core/src/test/java/types/temporal/generated/GeneratedConcreteNumberParityTest.java b/jmeos-core/src/test/java/types/temporal/generated/GeneratedConcreteNumberParityTest.java index 2f87dfd71..b2ca37d3a 100644 --- a/jmeos-core/src/test/java/types/temporal/generated/GeneratedConcreteNumberParityTest.java +++ b/jmeos-core/src/test/java/types/temporal/generated/GeneratedConcreteNumberParityTest.java @@ -95,6 +95,22 @@ void floatSurfaceMatchesTheLibrary() { assertEquals(GeneratedFunctions.tfloat_out(p, 6), g.out(6)); } + @Test + void intValueAccessorsMatchTheLibrary() { + TIntSeq a = new TIntSeq("[1@2019-09-01, 3@2019-09-02, 2@2019-09-03]"); + Pointer p = a.getInner(); + GeneratedTInt g = genInt(a); + + // valueN folds the bool+result to the base value; zero-based n reaches the one-based accessor. + Pointer r = GeneratedFunctions.tint_value_n(p, 2); + assertEquals(r == null ? null : Integer.valueOf(r.getInt(0)), g.valueN(1)); + + // valueAtTimestamptz folds the bool+result to the base value at a time. + OffsetDateTime t = OffsetDateTime.parse("2019-09-02T00:00:00Z"); + Pointer r2 = GeneratedFunctions.tint_value_at_timestamptz(p, t, true); + assertEquals(r2 == null ? null : Integer.valueOf(r2.getInt(0)), g.valueAtTimestamptz(t, true)); + } + @Test void intValueSplitMatchesTheLibrary() { TIntSeq a = new TIntSeq("[1@2019-09-01, 3@2019-09-02, 5@2019-09-03]");