PostgreSQL’s numeric type (hereafter pg numeric) offers higher precision than equivalent types in other databases, but its implementation is more complex and incurs relatively poor performance. Due to structural constraints, pg numeric cannot be effectively vectorized. As a result, in vectorized execution engines, it delivers no performance benefit — and often performs worse than non-vectorized execution due to compatibility-layer overhead. Compared to vectorized integer or floating-point types, pg numeric is typically 1–2 orders of magnitude slower.
To improve performance for numeric workloads, YMatrix introduces a bounded-precision numeric type (hereafter mxnumeric), supporting up to 38 digits of precision. This type is provided via the mxnumeric extension.
mxnumeric as the Default numeric TypeStarting with version 6.5, mxnumeric is installed and enabled by default as the numeric type. No additional installation or configuration is required: after database initialization, all references to numeric resolve to mxnumeric.
The mxnumeric type is implemented as public.numeric. The original pg_catalog.numeric remains available and unchanged.
Install the extension using the standard PostgreSQL extension command:
CREATE EXTENSION mxnumeric;
Upon successful installation, public.numeric is created in the current database. The built-in pg_catalog.numeric is unaffected.
When mxnumeric receives bug fixes or optimizations, upgrading usually requires only installing the new package — no manual intervention is needed.
However, if the extension adds or modifies functions, operators, or other catalog objects, administrators must manually upgrade the extension on existing clusters. The procedure varies depending on scope. Below are instructions assuming an upgrade from version 0.1 to 0.2.
To upgrade mxnumeric in an already-created database:
-- Connect to the target database
\c db1
-- Check current extension status and version
\dx mxnumeric
-- Method 1: Upgrade to a specific version
ALTER EXTENSION mxnumeric UPDATE TO '0.2';
-- Method 2: Upgrade to the latest available version
ALTER EXTENSION mxnumeric UPDATE;
Reinstalling the extension (DROP EXTENSION mxnumeric; CREATE EXTENSION mxnumeric;) also updates the version, but it drops all columns of type public.numeric due to dependency constraints. Avoid this method unless strictly necessary.
To ensure newly created databases automatically use the latest mxnumeric version, upgrade the extension in template databases:
-- Update mxnumeric in template1
\c template1
ALTER EXTENSION mxnumeric UPDATE;
-- Update mxnumeric in template0 (requires enabling connections first)
\c postgres
ALTER DATABASE template0 ALLOW_CONNECTIONS ON;
\c template0
ALTER EXTENSION mxnumeric UPDATE;
\c postgres
ALTER DATABASE template0 ALLOW_CONNECTIONS OFF;
After these steps, any newly created database will use the latest mxnumeric version by default.
Because both pg_catalog.numeric and public.numeric coexist, distinguishing which numeric type is in use is essential.
Two common methods are supported:
Check the GUC mx_force_system_types:
If mx_force_system_types = false, public.numeric is used.
SHOW mx_force_system_types;
mx_force_system_types
-----------------------
off
(1 row)
Check the oid of the numeric type:
If the oid is not 1700, then public.numeric is in use.
SELECT 'numeric'::regtype::oid;
oid
-------
13862
(1 row)
To determine whether numeric columns in a table use mxnumeric, use the helper functions:
SELECT attnum, attname, type FROM mxnumeric.list_numeric_columns('t1');
attnum | attname | type
--------+---------+--------------------
2 | col1 | pg_catalog.numeric
3 | col2 | public.numeric
(2 rows)
SELECT attnum, attname, type FROM mxnumeric.list_numeric_columns('public', 't1');
attnum | attname | type
--------+---------+--------------------
2 | col1 | pg_catalog.numeric
3 | col2 | public.numeric
(2 rows)
mx_force_system_typesSetting mx_force_system_types = false causes numeric to resolve to public.numeric. When mxnumeric is the default numeric type (v6.5+), this GUC defaults to false.
true: Uses pg_catalog.numeric. false: Uses public.numeric (i.e., mxnumeric).mxnumeric supports precision values from 1 to 38. Attempts to declare greater precision raise an error:
CREATE TABLE t2(a INT, b NUMERIC(39, 0));
ERROR: NUMERIC precision 39 must be between 1 and 38
Arithmetic results exceeding 38-digit precision trigger an overflow error:
-- Expected result: NUMERIC(40,0) → exceeds mxnumeric limit
SELECT 12345678901234567890::public.numeric * 12345678901234567890::public.numeric;
ERROR: value overflows numeric format (Error.cc:26)
-- pg_catalog.numeric handles it without error
SELECT 12345678901234567890::pg_catalog.numeric * 12345678901234567890::pg_catalog.numeric;
?column?
-----------------------------------------
152415787532388367501905199875019052100
SELECT pg_typeof(12345678901234567890::pg_catalog.numeric * 12345678901234567890::pg_catalog.numeric);
pg_typeof
-----------
numeric
When scale is set to 8 and precision to 38, operations yielding more than 8 fractional digits cause overflow:
-- pg_catalog.numeric supports up to 9 fractional digits
SELECT 123456789012345678901234567890.12345678::pg_catalog.numeric(38,8) * 0.9;
?column?
------------------------------------------
111111110111111111011111111101.111111102
(1 row)
-- public.numeric overflows
SELECT 123456789012345678901234567890.12345678::public.numeric(38,8) * 0.9;
ERROR: value overflows numeric format (Error.cc:26)
mxnumeric division returns DOUBLE PRECISION. In contrast, pg_catalog.numeric division returns NUMERIC.
Because SQL does not define precision requirements for numeric division, and because PostgreSQL mandates that division precision must be at least that of float8, mxnumeric uses float8 for division results to maximize performance.
This introduces minor precision loss relative to pg_catalog.numeric:
SELECT 1234.5678901234567890::pg_catalog.numeric(20,16) / 666;
?column?
--------------------
1.8537055407259111
(1 row)
-- mxnumeric division exhibits slight precision loss
SELECT 1234.5678901234567890::public.numeric(20,16) / 666;
?column?
--------------------
1.8537055407259109
The following functions return FLOAT8 instead of NUMERIC for improved performance:
exp() ln() log() pow() / power() sqrt() Some features cannot be enabled during extension creation and require explicit configuration.
The mxnumeric.set_config(text, bool) function enables or disables optional mxnumeric features. Current settings are visible in the mxnumeric.configs view:
SELECT * FROM mxnumeric.configs;
config | enabled | description
--------------------+---------+------------------------------------------------------------------------------------------------
linear_interpolate | f | replace original linear_interpolate functions with mxnumeric version to support mxnumeric type
mars3 | f | create operator classes to support mars3 btree & brin index for mxnumeric type
linear_interpolate Function SupportThe kernel’s linear_interpolate() function is hardcoded to accept only built-in types and does not natively support mxnumeric. Because usage is infrequent, mxnumeric does not enable this support by default.
To enable mxnumeric support for linear_interpolate(), call:
SELECT mxnumeric.set_config('linear_interpolate', true);
This operation:
pg_catalog.linear_interpolate() to preserve it,public.linear_interpolate() optimized for mxnumeric.To revert to the default behavior:
SELECT mxnumeric.set_config('linear_interpolate', false);
This restores pg_catalog.linear_interpolate() and drops the public version.
mars3 Index Supportmars3 and mxnumeric are independent extensions. Since mars3 installation order is not guaranteed, mxnumeric does not provide mars3_btree or mars3_brin operator classes by default.
To enable mxnumeric support for mars3 indexes, run:
SELECT mxnumeric.set_config('mars3', true);
Prerequisite: The
matrixtsextension must already be installed; otherwise, the command fails.
To disable support and drop the operator classes:
SELECT mxnumeric.set_config('mars3', false);
Ordinary functions operate independently of context (unlike aggregate functions, which apply only within GROUP BY clauses).
| oid | UDF Name | Target Schema | Lossy | Original Result Type | Optimized Result Type | Parameter Types | Result Precision | Function Type | Category | Alias Of |
|---|---|---|---|---|---|---|---|---|---|---|
| 1705 | abs |
public |
N | numeric |
numeric |
= |
plain |
math |
||
| 1711 | ceil |
public |
N | numeric |
numeric |
(P,0) |
plain |
math |
||
| 2167 | ceiling |
public |
N | numeric |
numeric |
(P,0) |
plain |
math |
ceil |
|
| 1973 | div |
public |
Y | numeric |
double |
numeric, numeric |
plain |
math |
||
| 1732 | exp |
public |
Y | numeric |
double |
numeric |
plain |
math |
||
| 1376 | factorial |
public |
N | numeric |
bigint |
(MAX,0) |
plain |
math |
||
| 1712 | floor |
public |
N | numeric |
numeric |
(P,0) |
plain |
math |
||
| 1734 | ln |
public |
Y | numeric |
double |
numeric |
plain |
math |
||
| 1736 | log |
public |
Y | numeric |
double |
numeric, numeric |
plain |
math |
||
| 1741 | log |
public |
Y | numeric |
double |
numeric |
plain |
math |
||
| 1481 | log10 |
? |
Y | numeric |
double |
numeric |
plain |
math |
||
| 1728 | mod |
public |
N | numeric |
numeric, numeric |
= |
plain |
math |
||
| 1704 | numeric_abs |
mxnumeric |
N | numeric |
numeric |
= |
plain |
math |
abs |
|
| 1724 | numeric_add |
mxnumeric |
N | numeric |
numeric, numeric |
+ |
plain |
math |
||
| 6997 | numeric_dec |
mxnumeric |
N | numeric |
numeric |
= |
plain |
math |
||
| 1727 | numeric_div |
mxnumeric |
Y | numeric |
double |
numeric, numeric |
plain |
math |
||
| 1980 | numeric_div_trunc |
mxnumeric |
Y | numeric |
double |
numeric, numeric |
plain |
math |
||
| 1733 | numeric_exp |
mxnumeric |
Y | numeric |
double |
numeric |
plain |
math |
exp |
|
| 111 | numeric_fac |
mxnumeric |
N | numeric |
bigint |
(MAX,0) |
plain |
math |
factorial |
|
| 1764 | numeric_inc |
mxnumeric |
N | numeric |
numeric |
= |
plain |
math |
||
| 1767 | numeric_larger |
mxnumeric |
N | numeric |
numeric, numeric |
= |
plain |
math |
||
| 1735 | numeric_ln |
mxnumeric |
Y | numeric |
double |
numeric |
plain |
math |
ln |
|
| 1737 | numeric_log |
mxnumeric |
Y | numeric |
double |
numeric |
plain |
math |
log |
|
| 1729 | numeric_mod |
mxnumeric |
N | numeric |
numeric, numeric |
= |
plain |
math |
mod |
|
| 1726 | numeric_mul |
mxnumeric |
N | numeric |
numeric, numeric |
* |
plain |
math |
||
| 1739 | numeric_power |
mxnumeric |
Y | numeric |
double |
numeric, numeric |
plain |
math |
power |
|
| 1766 | numeric_smaller |
mxnumeric |
N | numeric |
numeric, numeric |
= |
plain |
math |
||
| 1731 | numeric_sqrt |
mxnumeric |
Y | numeric |
double |
numeric |
plain |
math |
sqrt |
|
| 1725 | numeric_sub |
mxnumeric |
N | numeric |
numeric, numeric |
+ |
plain |
math |
||
| 1771 | numeric_uminus |
mxnumeric |
N | numeric |
numeric |
= |
plain |
math |
||
| 1915 | numeric_uplus |
mxnumeric |
N | numeric |
numeric |
= |
plain |
math |
||
| 1738 | pow |
public |
Y | numeric |
double |
numeric |
plain |
math |
power |
|
| 2169 | power |
public |
Y | numeric |
double |
numeric |
plain |
math |
||
| 1707 | round |
public |
N | numeric |
numeric, integer |
(P,arg) |
plain |
math |
||
| 1708 | round |
public |
N | numeric |
numeric |
(P,0) |
plain |
math |
||
| 1730 | sqrt |
public |
Y | numeric |
double |
numeric |
plain |
math |
||
| 1709 | trunc |
public |
N | numeric |
numeric, integer |
(P,arg) |
plain |
math |
||
| 1710 | trunc |
public |
N | numeric |
numeric |
(P,0) |
plain |
math |
numeric_larger() and numeric_smaller() support MIN()/MAX() aggregates and assume identical input precisions. numeric_uminus() and numeric_uplus() implement unary +x and -x. log10(x)) are SQL-language wrappers (e.g., log(10,x)). Because mxnumeric restricts language to C, dedicated C implementations are provided.| oid | UDF Name | Target Schema | Lossy | Original Result Type | Optimized Result Type | Parameter Types | Result Precision | Function Type | Category |
|---|---|---|---|---|---|---|---|---|---|
| 1769 | numeric_cmp |
mxnumeric |
integer |
numeric, numeric |
plain |
oper |
|||
| 1718 | numeric_eq |
mxnumeric |
bool |
numeric, numeric |
plain |
oper |
|||
| 1721 | numeric_ge |
mxnumeric |
bool |
numeric, numeric |
plain |
oper |
|||
| 1720 | numeric_gt |
mxnumeric |
bool |
numeric, numeric |
plain |
oper |
|||
| 1723 | numeric_le |
mxnumeric |
bool |
numeric, numeric |
plain |
oper |
|||
| 1722 | numeric_lt |
mxnumeric |
bool |
numeric, numeric |
plain |
oper |
|||
| 1719 | numeric_ne |
mxnumeric |
bool |
numeric, numeric |
plain |
oper |
| oid | UDF Name | Target Schema | Lossy | Original Result Type | Optimized Result Type | Parameter Types | Result Precision | Function Type | Category |
|---|---|---|---|---|---|---|---|---|---|
| 6145 | cdblegacyhash_numeric |
public |
integer |
numeric |
plain |
index |
|||
| 432 | hash_numeric |
public |
integer |
numeric |
plain |
index |
|||
| 780 | hash_numeric_extended |
public |
bigint |
numeric |
plain |
index |
|||
| 4141 | in_range |
public |
bool |
numeric, numeric, numeric, boolean, boolean |
plain |
index |
| oid | UDF Name | Target Schema | Lossy | Original Result Type | Optimized Result Type | Parameter Types | Result Precision | Function Type | Category |
|---|---|---|---|---|---|---|---|---|---|
| 1745 | float4 |
public |
real |
numeric |
plain |
cast |
|||
| 1746 | float8 |
public |
double |
numeric |
plain |
cast |
|||
| 1783 | int2 |
public |
smallint |
numeric |
plain |
cast |
|||
| 1744 | int4 |
public |
integer |
numeric |
plain |
cast |
|||
| 1779 | int8 |
public |
bigint |
numeric |
plain |
cast |
|||
| 3824 | money |
public |
money |
numeric |
plain |
cast |
|||
| 1703 | numeric |
public |
numeric |
numeric, int |
plain |
cast |
|||
| 1740 | numeric |
public |
numeric |
integer |
plain |
cast |
|||
| 1742 | numeric |
public |
numeric |
real |
plain |
cast |
|||
| 1743 | numeric |
public |
numeric |
double |
plain |
cast |
|||
| 1781 | numeric |
public |
numeric |
bigint |
plain |
cast |
|||
| 1782 | numeric |
public |
numeric |
smallint |
plain |
cast |
|||
| 3449 | numeric |
public |
numeric |
jsonb |
plain |
cast |
|||
| 3823 | numeric |
public |
numeric |
money |
plain |
cast |
|||
| 7597 | numeric2complex |
public |
complex |
numeric |
plain |
cast |
|||
| 3844 | numrange |
public |
numrange |
numeric, numeric |
plain |
cast |
|||
| 3845 | numrange |
public |
numrange |
numeric, numeric, text |
plain |
cast |
|||
| 3924 | numrange_subdiff |
public |
double |
numeric, numeric |
plain |
cast |
|||
| 1772 | to_char |
public |
text |
numeric, text |
plain |
cast |
|||
| 1777 | to_number |
public |
numeric |
text, text |
plain |
cast |
| oid | UDF Name | Target Schema | Lossy | Original Result Type | Optimized Result Type | Parameter Types | Result Precision | Function Type | Category |
|---|---|---|---|---|---|---|---|---|---|
| 3259 | generate_series |
public |
N | numeric |
numeric, numeric, numeric |
= |
plain |
util |
|
| 3260 | generate_series |
public |
N | numeric |
numeric, numeric |
= |
plain |
util |
|
| 3281 | scale |
public |
integer |
numeric |
plain |
util |
|||
| 1706 | sign |
public |
N | numeric |
numeric |
= |
plain |
util |
|
| 2170 | width_bucket |
public |
integer |
numeric, numeric, numeric, integer |
plain |
util |
| oid | UDF Name | Target Schema | Lossy | Original Result Type | Optimized Result Type | Parameter Types | Result Precision | Function Type | Category |
|---|---|---|---|---|---|---|---|---|---|
| 7082 | interval_bound |
public |
N | numeric |
numeric, numeric |
= |
plain |
internal |
|
| 7083 | interval_bound |
public |
N | numeric |
numeric, numeric, integer |
= |
plain |
internal |
|
| 7084 | interval_bound |
public |
N | numeric |
numeric, numeric, integer, numeric |
= |
plain |
internal |
|
| 6082 | linear_interpolate |
public |
numeric |
anyelement, anyelement, numeric, anyelement, numeric |
plain |
internal |
|||
| 1701 | numeric_in |
mxnumeric |
numeric |
cstring, oid, integer |
(P,S) |
plain |
internal |
||
| 1702 | numeric_out |
mxnumeric |
cstring |
numeric |
plain |
internal |
|||
| 2460 | numeric_recv |
mxnumeric |
numeric |
internal, oid, integer |
(P,S) |
plain |
internal |
||
| 2461 | numeric_send |
mxnumeric |
bytea |
numeric |
plain |
internal |
|||
| 3237 | pg_lsn_mi |
? |
N | numeric |
pg_lsn, pg_lsn |
plain |
internal |
||
| 3166 | pg_size_pretty |
? |
text |
numeric |
plain |
internal |
|||
| 2022 | pg_stat_get_activity |
? |
record |
integer |
plain |
internal |
|||
| 1136 | pg_stat_get_wal |
? |
record |
plain |
internal |
||||
| 3165 | pg_wal_lsn_diff |
? |
numeric |
pg_lsn, pg_lsn |
plain |
internal |
| oid | UDF Name | Schema | Lossy | Original Result Type | Optimized Result Type | Parameter Types | Result Precision | Function Type | Category |
|---|---|---|---|---|---|---|---|---|---|
| 2100 | avg |
public |
numeric |
bigint |
(MAX, S) |
agg |
|||
| 2101 | avg |
public |
numeric |
integer |
(MAX, S) |
agg |
|||
| 2102 | avg |
public |
numeric |
smallint |
(MAX, S) |
agg |
|||
| 2103 | avg |
public |
numeric |
numeric |
(MAX, S) |
agg |
|||
| 2130 | max |
public |
numeric |
numeric |
= |
agg |
|||
| 2146 | min |
public |
numeric |
numeric |
= |
agg |
|||
| 2154 | stddev |
public |
numeric |
bigint |
agg |
||||
| 2155 | stddev |
public |
numeric |
integer |
agg |
||||
| 2156 | stddev |
public |
numeric |
smallint |
agg |
||||
| 2159 | stddev |
public |
numeric |
numeric |
agg |
||||
| 2724 | stddev_pop |
public |
numeric |
bigint |
agg |
||||
| 2725 | stddev_pop |
public |
numeric |
integer |
agg |
||||
| 2726 | stddev_pop |
public |
numeric |
smallint |
agg |
||||
| 2729 | stddev_pop |
public |
numeric |
numeric |
agg |
||||
| 2712 | stddev_samp |
public |
numeric |
bigint |
agg |
||||
| 2713 | stddev_samp |
public |
numeric |
integer |
agg |
||||
| 2714 | stddev_samp |
public |
numeric |
smallint |
agg |
||||
| 2717 | stddev_samp |
public |
numeric |
numeric |
agg |
||||
| 2107 | sum |
public |
numeric |
bigint |
(MAX, S) |
agg |
|||
| 2114 | sum |
public |
numeric |
numeric |
(MAX, S) |
agg |
|||
| 2718 | var_pop |
public |
numeric |
bigint |
agg |
||||
| 2719 | var_pop |
public |
numeric |
integer |
agg |
||||
| 2720 | var_pop |
public |
numeric |
smallint |
agg |
||||
| 2723 | var_pop |
public |
numeric |
numeric |
agg |
||||
| 2641 | var_samp |
public |
numeric |
bigint |
agg |
||||
| 2642 | var_samp |
public |
numeric |
integer |
agg |
||||
| 2643 | var_samp |
public |
numeric |
smallint |
agg |
||||
| 2646 | var_samp |
public |
numeric |
numeric |
agg |
||||
| 2148 | variance |
public |
numeric |
bigint |
agg |
||||
| 2149 | variance |
public |
numeric |
integer |
agg |
||||
| 2150 | variance |
public |
numeric |
smallint |
agg |
||||
| 2153 | variance |
public |
numeric |
numeric |
agg |
| oid | UDF Name | Schema | Lossy | Original Result Type | Optimized Result Type | Parameter Types | Result Precision | Function Type | Category |
|---|---|---|---|---|---|---|---|---|---|
| 1964 | int8_avg |
public |
numeric |
bigint[] |
agg helper |
legacy |
|||
| 1842 | int8_sum |
public |
numeric |
numeric, bigint |
agg helper |
legacy |
|||
| 1833 | numeric_accum |
mxnumeric |
internal |
internal, numeric |
agg helper |
||||
| 3548 | numeric_accum_inv |
mxnumeric |
internal |
internal, numeric |
agg helper |
||||
| 1837 | numeric_avg |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 2858 | numeric_avg_accum |
mxnumeric |
internal |
internal |
agg helper |
||||
| 3389 | numeric_poly_avg |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 3392 | numeric_poly_stddev_pop |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 3393 | numeric_poly_stddev_samp |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 3388 | numeric_poly_sum |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 3390 | numeric_poly_var_pop |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 3391 | numeric_poly_var_samp |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 2596 | numeric_stddev_pop |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 1839 | numeric_stddev_samp |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 3178 | numeric_sum |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 2514 | numeric_var_pop |
mxnumeric |
numeric |
internal |
agg helper |
||||
| 1838 | numeric_var_samp |
mxnumeric |
numeric |
internal |
agg helper |