Skip to content

fix(type): validate DecimalType construction and drop the init setters - #19

Merged
CurtHagenlocher merged 1 commit into
mainfrom
fix/17-validate-decimaltype-construction
Aug 22, 2026
Merged

fix(type): validate DecimalType construction and drop the init setters#19
CurtHagenlocher merged 1 commit into
mainfrom
fix/17-validate-decimaltype-construction

Conversation

@CurtHagenlocher

Copy link
Copy Markdown
Contributor

Fixes #17.

DecimalType.Numeric validated; the record's generated constructor and init setters did not, so both of these produced NUMERIC(10,30) with IntegerDigits = -20:

new DecimalType(10, 30)
new DecimalType(10, 2) { Scale = 30 }

That value is not inert — it feeds DecimalTypeRules.Clamp and the Width tier selection, so it propagates into the arithmetic instead of failing at construction.

What changed

Options 1 and 3 from the issue, together. The positional record declaration is replaced with explicit get-only Precision/Scale, a validating constructor, and a hand-written Deconstruct:

  • the constructor rejects precision outside 1..76 and scale above precision, so Numeric is now an alias for it rather than the only validating door;
  • with no init setters, the object-initializer bypass is gone entirely;
  • Numeric keeps its int parameters and checks them first, so Numeric(300, 0) is still rejected rather than truncated to precision 44 by the conversion to byte.

Cost is two comparisons at construction, behind NoInlining throw helpers so the constructor stays inlinable. Nothing in the library constructs a DecimalType per element — the kernels take the types as arguments — and the only in-repo construction site is DecimalTypeRules.Clamp, once per operation.

The remaining unvalidated value is default(DecimalType), which a struct always permits; it is now documented on the type as behaving like NUMERIC(0,0).

Compatibility

Source- and binary-breaking for anyone who used the init setters (an object initializer or with) — the removed set_Precision/set_Scale are gone from the public surface. The positional constructor keeps its signature and now throws for inputs it previously accepted. Nothing in this repo used either route.

AssemblyVersion is deliberately left at 0.3.0.0 — the csproj comment says to bump it to 0.4.0.0 at the next breaking release, and that call (plus whether this ships alone or with other breaks) is yours.

Tests

Six new cases in DecimalTypeTests: constructor rejection for scale > precision, precision 0, and precision 77 (each asserting the ParamName), constructor/Numeric agreement, Deconstruct, and an exhaustive sweep over all 1..76 precisions asserting IntegerDigits >= 0 for every constructible type and that scale = precision + 1 throws. Full suite passes on net8.0, net10.0, and net472 (549 tests).

DecimalType.Numeric validated; the record's generated constructor and init
setters did not, so `new DecimalType(10, 30)` and
`new DecimalType(10, 2) { Scale = 30 }` both produced NUMERIC(10,30) with
IntegerDigits = -20 — a value that feeds DecimalTypeRules.Clamp and the width
selection rather than failing at construction.

Replace the positional record declaration with explicit get-only properties, a
validating constructor, and a hand-written Deconstruct. The constructor now
rejects precision outside 1..76 and scale above precision (two comparisons,
behind non-inlined throw helpers), and with no init setters the
object-initializer route is gone. Numeric keeps its int overload so
out-of-range values are rejected before the narrowing conversion to byte, and
is otherwise an alias for the constructor.

Fixes #17
@CurtHagenlocher
CurtHagenlocher merged commit 399081a into main Aug 22, 2026
3 checks passed
@CurtHagenlocher
CurtHagenlocher deleted the fix/17-validate-decimaltype-construction branch August 22, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(type): the public constructor and init setters bypass Numeric's validation, so NUMERIC(10,30) with IntegerDigits=-20 is constructible

1 participant