Skip to content

Bare ::numeric / CAST(... AS numeric) cast (no typmod) truncates to scale 0 #85

@StefanSteiner

Description

@StefanSteiner

Summary

A cast to unconstrained NUMERIC (no precision/scale typmod) rounds the value to an integer (scale 0) instead of preserving the fractional digits. Specifying a typmod works correctly.

Discovered alongside #84 (the negative-sub-unit Display sign bug) during an analysis session; filing separately so it can be tracked after #84 closes.

Reproduction

SELECT
  0.859::numeric        AS bare_cast,    -- returns 1.0    (expected 0.859)
  CAST(0.859 AS numeric) AS bare_cast2,  -- returns 1.0    (expected 0.859)
  123.456::numeric      AS bare_big,     -- returns 123.0  (expected 123.456)
  0.859::numeric(10,4)  AS with_typmod;  -- returns 0.859  (correct)

For comparison, PostgreSQL treats unconstrained numeric as "any precision/scale" and preserves 0.859.

Why this matters

It is an easy, silent footgun in analytical SQL: AVG(...)::numeric, (a/b)::numeric, correlation/ratio expressions, etc. all quietly round to integers unless the author remembers to add a typmod. Combined with #84 it produced sign- and magnitude-wrong numbers in an LLM-driven analysis.

Open question / where to look

Need to determine the layer:

  1. Is hyperd itself returning the cast result typed as NUMERIC(_, 0) for an unconstrained cast, or returning a value already rounded to scale 0?
  2. Or is the Rust API defaulting scale = 0 when the RowDescription typmod is -1 (unconstrained) during SqlType::Numeric { scale, .. } parsing? See the numeric scale extraction in the protocol/types layer and how row.get::<Numeric>() obtains scale.

If (2), the fix is to carry through the actual decimal scale (or treat unconstrained numeric as a high default scale) rather than defaulting to 0. If (1), it is an upstream hyperd semantics question.

Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions