Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 - bmax(scale_a, scale_b)
a * bscale_a + scale_b
a / bmax(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_supportedp>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_totallegacy 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-движком.