Skip to content

Commit 20d6225

Browse files
committed
Add functions min_scale(numeric) and trim_scale(numeric).
These allow better control of trailing zeroes in numeric values. Pavel Stehule, based on an old proposal of Marko Tiikkaja's; review by Karl Pinc Discussion: https://postgr.es/m/CAFj8pRDjs-navGASeF0Wk74N36YGFJ+v=Ok9_knRa7vDc-qugg@mail.gmail.com
1 parent b9c130a commit 20d6225

File tree

6 files changed

+285
-3
lines changed

6 files changed

+285
-3
lines changed

doc/src/sgml/func.sgml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,20 @@
918918
<entry><literal>6.0000000000</literal></entry>
919919
</row>
920920

921+
<row>
922+
<entry>
923+
<indexterm>
924+
<primary>min_scale</primary>
925+
</indexterm>
926+
<literal><function>min_scale(<type>numeric</type>)</function></literal>
927+
</entry>
928+
<entry><type>integer</type></entry>
929+
<entry>minimum scale (number of fractional decimal digits) needed
930+
to represent the supplied value</entry>
931+
<entry><literal>min_scale(8.4100)</literal></entry>
932+
<entry><literal>2</literal></entry>
933+
</row>
934+
921935
<row>
922936
<entry>
923937
<indexterm>
@@ -1011,8 +1025,8 @@
10111025
</entry>
10121026
<entry><type>integer</type></entry>
10131027
<entry>scale of the argument (the number of decimal digits in the fractional part)</entry>
1014-
<entry><literal>scale(8.41)</literal></entry>
1015-
<entry><literal>2</literal></entry>
1028+
<entry><literal>scale(8.4100)</literal></entry>
1029+
<entry><literal>4</literal></entry>
10161030
</row>
10171031

10181032
<row>
@@ -1041,6 +1055,20 @@
10411055
<entry><literal>1.4142135623731</literal></entry>
10421056
</row>
10431057

1058+
<row>
1059+
<entry>
1060+
<indexterm>
1061+
<primary>trim_scale</primary>
1062+
</indexterm>
1063+
<literal><function>trim_scale(<type>numeric</type>)</function></literal>
1064+
</entry>
1065+
<entry><type>numeric</type></entry>
1066+
<entry>reduce the scale (number of fractional decimal digits) by
1067+
removing trailing zeroes</entry>
1068+
<entry><literal>trim_scale(8.4100)</literal></entry>
1069+
<entry><literal>8.41</literal></entry>
1070+
</row>
1071+
10441072
<row>
10451073
<entry>
10461074
<indexterm>

src/backend/utils/adt/numeric.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3179,6 +3179,97 @@ numeric_scale(PG_FUNCTION_ARGS)
31793179
PG_RETURN_INT32(NUMERIC_DSCALE(num));
31803180
}
31813181

3182+
/*
3183+
* Calculate minimum scale for value.
3184+
*/
3185+
static int
3186+
get_min_scale(NumericVar *var)
3187+
{
3188+
int min_scale;
3189+
int last_digit_pos;
3190+
3191+
/*
3192+
* Ordinarily, the input value will be "stripped" so that the last
3193+
* NumericDigit is nonzero. But we don't want to get into an infinite
3194+
* loop if it isn't, so explicitly find the last nonzero digit.
3195+
*/
3196+
last_digit_pos = var->ndigits - 1;
3197+
while (last_digit_pos >= 0 &&
3198+
var->digits[last_digit_pos] == 0)
3199+
last_digit_pos--;
3200+
3201+
if (last_digit_pos >= 0)
3202+
{
3203+
/* compute min_scale assuming that last ndigit has no zeroes */
3204+
min_scale = (last_digit_pos - var->weight) * DEC_DIGITS;
3205+
3206+
/*
3207+
* We could get a negative result if there are no digits after the
3208+
* decimal point. In this case the min_scale must be zero.
3209+
*/
3210+
if (min_scale > 0)
3211+
{
3212+
/*
3213+
* Reduce min_scale if trailing digit(s) in last NumericDigit are
3214+
* zero.
3215+
*/
3216+
NumericDigit last_digit = var->digits[last_digit_pos];
3217+
3218+
while (last_digit % 10 == 0)
3219+
{
3220+
min_scale--;
3221+
last_digit /= 10;
3222+
}
3223+
}
3224+
else
3225+
min_scale = 0;
3226+
}
3227+
else
3228+
min_scale = 0; /* result if input is zero */
3229+
3230+
return min_scale;
3231+
}
3232+
3233+
/*
3234+
* Returns minimum scale required to represent supplied value without loss.
3235+
*/
3236+
Datum
3237+
numeric_min_scale(PG_FUNCTION_ARGS)
3238+
{
3239+
Numeric num = PG_GETARG_NUMERIC(0);
3240+
NumericVar arg;
3241+
int min_scale;
3242+
3243+
if (NUMERIC_IS_NAN(num))
3244+
PG_RETURN_NULL();
3245+
3246+
init_var_from_num(num, &arg);
3247+
min_scale = get_min_scale(&arg);
3248+
free_var(&arg);
3249+
3250+
PG_RETURN_INT32(min_scale);
3251+
}
3252+
3253+
/*
3254+
* Reduce scale of numeric value to represent supplied value without loss.
3255+
*/
3256+
Datum
3257+
numeric_trim_scale(PG_FUNCTION_ARGS)
3258+
{
3259+
Numeric num = PG_GETARG_NUMERIC(0);
3260+
Numeric res;
3261+
NumericVar result;
3262+
3263+
if (NUMERIC_IS_NAN(num))
3264+
PG_RETURN_NUMERIC(make_result(&const_nan));
3265+
3266+
init_var_from_num(num, &result);
3267+
result.dscale = get_min_scale(&result);
3268+
res = make_result(&result);
3269+
free_var(&result);
3270+
3271+
PG_RETURN_NUMERIC(res);
3272+
}
31823273

31833274

31843275
/* ----------------------------------------------------------------------

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 201911241
56+
#define CATALOG_VERSION_NO 202001061
5757

5858
#endif

src/include/catalog/pg_proc.dat

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4254,6 +4254,13 @@
42544254
{ oid => '3281', descr => 'number of decimal digits in the fractional part',
42554255
proname => 'scale', prorettype => 'int4', proargtypes => 'numeric',
42564256
prosrc => 'numeric_scale' },
4257+
{ oid => '8389', descr => 'minimum scale needed to represent the value',
4258+
proname => 'min_scale', prorettype => 'int4', proargtypes => 'numeric',
4259+
prosrc => 'numeric_min_scale' },
4260+
{ oid => '8390',
4261+
descr => 'numeric with minimum scale needed to represent the value',
4262+
proname => 'trim_scale', prorettype => 'numeric', proargtypes => 'numeric',
4263+
prosrc => 'numeric_trim_scale' },
42574264
{ oid => '1740', descr => 'convert int4 to numeric',
42584265
proname => 'numeric', prorettype => 'numeric', proargtypes => 'int4',
42594266
prosrc => 'int4_numeric' },

src/test/regress/expected/numeric.out

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,6 +2078,132 @@ select scale(-13.000000000000000);
20782078
15
20792079
(1 row)
20802080

2081+
--
2082+
-- Tests for min_scale()
2083+
--
2084+
select min_scale(numeric 'NaN') is NULL; -- should be true
2085+
?column?
2086+
----------
2087+
t
2088+
(1 row)
2089+
2090+
select min_scale(0); -- no digits
2091+
min_scale
2092+
-----------
2093+
0
2094+
(1 row)
2095+
2096+
select min_scale(0.00); -- no digits again
2097+
min_scale
2098+
-----------
2099+
0
2100+
(1 row)
2101+
2102+
select min_scale(1.0); -- no scale
2103+
min_scale
2104+
-----------
2105+
0
2106+
(1 row)
2107+
2108+
select min_scale(1.1); -- scale 1
2109+
min_scale
2110+
-----------
2111+
1
2112+
(1 row)
2113+
2114+
select min_scale(1.12); -- scale 2
2115+
min_scale
2116+
-----------
2117+
2
2118+
(1 row)
2119+
2120+
select min_scale(1.123); -- scale 3
2121+
min_scale
2122+
-----------
2123+
3
2124+
(1 row)
2125+
2126+
select min_scale(1.1234); -- scale 4, filled digit
2127+
min_scale
2128+
-----------
2129+
4
2130+
(1 row)
2131+
2132+
select min_scale(1.12345); -- scale 5, 2 NDIGITS
2133+
min_scale
2134+
-----------
2135+
5
2136+
(1 row)
2137+
2138+
select min_scale(1.1000); -- 1 pos in NDIGITS
2139+
min_scale
2140+
-----------
2141+
1
2142+
(1 row)
2143+
2144+
select min_scale(1e100); -- very big number
2145+
min_scale
2146+
-----------
2147+
0
2148+
(1 row)
2149+
2150+
--
2151+
-- Tests for trim_scale()
2152+
--
2153+
select trim_scale(numeric 'NaN');
2154+
trim_scale
2155+
------------
2156+
NaN
2157+
(1 row)
2158+
2159+
select trim_scale(1.120);
2160+
trim_scale
2161+
------------
2162+
1.12
2163+
(1 row)
2164+
2165+
select trim_scale(0);
2166+
trim_scale
2167+
------------
2168+
0
2169+
(1 row)
2170+
2171+
select trim_scale(0.00);
2172+
trim_scale
2173+
------------
2174+
0
2175+
(1 row)
2176+
2177+
select trim_scale(1.1234500);
2178+
trim_scale
2179+
------------
2180+
1.12345
2181+
(1 row)
2182+
2183+
select trim_scale(110123.12475871856128000);
2184+
trim_scale
2185+
-----------------------
2186+
110123.12475871856128
2187+
(1 row)
2188+
2189+
select trim_scale(-1123.124718561280000000);
2190+
trim_scale
2191+
-------------------
2192+
-1123.12471856128
2193+
(1 row)
2194+
2195+
select trim_scale(-13.00000000000000000000);
2196+
trim_scale
2197+
------------
2198+
-13
2199+
(1 row)
2200+
2201+
select trim_scale(1e100);
2202+
trim_scale
2203+
-------------------------------------------------------------------------------------------------------
2204+
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
2205+
(1 row)
2206+
20812207
--
20822208
-- Tests for SUM()
20832209
--

src/test/regress/sql/numeric.sql

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,36 @@ select scale(110123.12475871856128);
10361036
select scale(-1123.12471856128);
10371037
select scale(-13.000000000000000);
10381038

1039+
--
1040+
-- Tests for min_scale()
1041+
--
1042+
1043+
select min_scale(numeric 'NaN') is NULL; -- should be true
1044+
select min_scale(0); -- no digits
1045+
select min_scale(0.00); -- no digits again
1046+
select min_scale(1.0); -- no scale
1047+
select min_scale(1.1); -- scale 1
1048+
select min_scale(1.12); -- scale 2
1049+
select min_scale(1.123); -- scale 3
1050+
select min_scale(1.1234); -- scale 4, filled digit
1051+
select min_scale(1.12345); -- scale 5, 2 NDIGITS
1052+
select min_scale(1.1000); -- 1 pos in NDIGITS
1053+
select min_scale(1e100); -- very big number
1054+
1055+
--
1056+
-- Tests for trim_scale()
1057+
--
1058+
1059+
select trim_scale(numeric 'NaN');
1060+
select trim_scale(1.120);
1061+
select trim_scale(0);
1062+
select trim_scale(0.00);
1063+
select trim_scale(1.1234500);
1064+
select trim_scale(110123.12475871856128000);
1065+
select trim_scale(-1123.124718561280000000);
1066+
select trim_scale(-13.00000000000000000000);
1067+
select trim_scale(1e100);
1068+
10391069
--
10401070
-- Tests for SUM()
10411071
--

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy