NUMERIC / DECIMAL — точный фиксированный тип (Phase 0)
RM-0.6.7.16 (RFC-2026-516). Начиная с этого релиза
NUMERIC(p,s)/DECIMAL(p,s)хранятся и считаются точно (128-bit fixed-point черезrust_decimal), а не как приближение поверхdouble. Покрывает деньги/биллинг/ERP до 28 значащих цифр.
Что это даёт оператору
- Точные деньги.
SUM,AVG,+ - * /надNUMERICдают exact-результат, без float-дрейфа:0.10 + 0.20 = 0.30(а не0.30000000000000004). - Хранимый scale значим.
INSERT ... VALUES (12.5)в колонкуNUMERIC(10,2)хранится и выводится как12.50(округление round-half-away-from-zero, как в PostgreSQL). - pgwire. Колонка отдаётся клиенту как PostgreSQL
numeric(OID 1700); текстовый формат значения несёт фиксированный scale.
DDL
CREATE TABLE invoice (
id INT,
amount NUMERIC(18,2), -- точные деньги
rate DECIMAL(10,6) -- синоним NUMERIC
);
Правила typmod:
1 ≤ precision ≤ 28,0 ≤ scale ≤ precision.NUMERIC(18, 2)(пробелы внутри скобок) — допустимо.NUMERICбез typmod — без ограничения precision/scale.
Арифметика и масштаб (scale)
| Операция | scale результата |
|---|---|
a + b, a - b | max(scale_a, scale_b) |
a * b | scale_a + scale_b |
a / b | max(scale_a, 6) |
При записи в колонку значение приводится к её scale округлением round-half-away-from-zero
(-0.5 → -1, 0.5 → 1).
SQLSTATE (что увидит DBA)
| SQLSTATE | Когда |
|---|---|
22003 numeric_value_out_of_range | переполнение (> 96-bit / p>28 диапазона), а также cast NaN/Infinity → numeric |
22012 division_by_zero | деление на ноль |
22P02 invalid_text_representation | 'abc'::numeric (невалидный текст) |
0A000 feature_not_supported | p>28 в DDL; 'NaN'::numeric; индекс по numeric-колонке (см. ниже) |
22023 invalid_parameter_value | невалидный typmod (scale>precision, precision=0) |
Ограничение Phase 0: индексы по NUMERIC
Индекс по NUMERIC/DECIMAL-колонке в Phase 0 не поддержан — CREATE INDEX на такой
колонке возвращает 0A000 feature_not_supported. Диапазонные запросы по numeric идут
seq-scan’ом. Упорядоченный индекс по decimal планируется в v0.7 (TD-2026-0428).
Метрики (Prometheus)
| Метрика | Значение |
|---|---|
angarabase_decimal_overflow_total | переполнения/NaN-Inf cast → 22003 |
angarabase_decimal_legacy_floatread_total | legacy float-backed numeric-значения, прочитанные и нормализованные в Decimal (см. миграцию) |
angarabase_decimal_index_fallback_total | отклонённые fail-closed запросы индекса по numeric (0A000) |
PromQL пример (рост лоссовых чтений старых данных):
rate(angarabase_decimal_legacy_floatread_total[5m])
Миграция существующих NUMERIC-данных
Колонки NUMERIC, созданные до Phase 0, хранили значения как double (float-backed).
После апгрейда они читаются через dual-read: при чтении такое значение нормализуется в
Decimal (счётчик angarabase_decimal_legacy_floatread_total).
⚠️ Важно: dual-read значения наследуют исходную float-неточность — точность гарантируется только для строк, записанных после Phase 0. Колонка со смешанными старыми (float) и новыми (exact) строками считается единообразно как Decimal, но старые значения остаются настолько точными, насколько были во float. Чтобы сделать историю точной — перезапишите её (
UPDATE) после апгрейда.
Вне scope Phase 0 (горизонт v0.7)
- Binary-формат pgwire NUMERIC (NBASE-10000) — сейчас text-wire.
- Индексируемый decimal (order-preserving).
- Arbitrary precision > 28 цифр (varlena).
- Decimal-SIMD в векторном движке — numeric-агрегаты считаются точным row-движком.