Skip to content

ToSqlParam: support scaled Numeric (scale > 0) params — needs text-format bind #132

@StefanSteiner

Description

@StefanSteiner

Summary

impl ToSqlParam for Numeric (added in #65) only supports scale = 0
(whole numbers). Scaled decimals (scale > 0) are rejected because Hyper
cannot read PG-binary NUMERIC parameters that require scale reconciliation.
This issue tracks lifting that limitation.

Background — why scaled Numeric params don't work today

query_params sends all parameters over the PostgreSQL extended-query
binary bind path (format code 1, hardcoded in
connection.rs:517-519).
For NUMERIC, the binary form is the PostgreSQL nbase-10000 packed-digit
wire format.

Empirically (probed against hyperd 0.0.25080):

Param Result
Numeric scale 0 (e.g. 42) ✅ accepted
Numeric scale > 0 (e.g. 12345.6700) ERROR: Hyper cannot handle truncation when reading numerics. (0A000)
same, with explicit CAST($1 AS numeric(38,4)) ❌ same error

The error is SQLSTATE 0A000 (feature_not_supported) and fires
regardless of the encoded dscale/weight or an explicit target type —
so it's a Hyper server limitation on reading binary NUMERIC params,
not a client-side encoding bug.

To avoid shipping something that looks supported but breaks on real
decimal data, #65 made Numeric::encode_param return an error (or the
impl rejects) when scale() > 0, failing fast with a clear message
instead of the cryptic server error.

Possible paths to full support

  1. Text-format bind for Numeric. Allow per-parameter format codes so
    a Numeric param can travel as text (format code 0) instead of
    binary. Hyper parses '12345.6700'::numeric fine from text. This
    requires plumbing per-parameter format codes through
    start_execute_prepared / frontend::bind (currently
    param_formats is a uniform vec![1; n]). This is the most likely
    fix and is self-contained to the core client.
  2. Upstream Hyper fix. If/when hyperd learns to read scaled binary
    NUMERIC params, the existing binary encoder can be extended to emit
    the full nbase-10000 format and the scale guard removed.
  3. Hybrid. Keep binary for scale=0 (fast path) and fall back to text
    for scale>0.

Recommended: option 1 (per-parameter text format), since it also unlocks
clean text binding for any future type Hyper can't read in binary.

Acceptance criteria

  • A Numeric with scale > 0 can be bound via query_params and
    round-trips correctly (e.g. 12345.6700 in → 12345.6700 out).
  • The scale=0 fast path is preserved (no regression).
  • A test covers several scales (0, 2, 4, 10) and negative values.

Related

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