po 9. 12. 2019 v 3:51 odesílatel Karl O. Pinc <k...@meme.com> napsal:

> Hi Pavel,
>
> Thanks for your changes.  More inline below:
>
> On Sun, 8 Dec 2019 08:38:38 +0100
> Pavel Stehule <pavel.steh...@gmail.com> wrote:
>
> > ne 8. 12. 2019 v 2:23 odesílatel Karl O. Pinc <k...@meme.com> napsal:
>
> > > On Mon, 11 Nov 2019 15:47:37 +0100
> > > Pavel Stehule <pavel.steh...@gmail.com> wrote:
>
> > > > I implemented two functions - first minscale, second trim_scale.
> > > > The overhead of second is minimal - so I think it can be good to
> > > > have it. I started design with the name "trim_scale", but the
> > > > name can be any other.
>
>
> > > I comment on various hunks in line below:
>
> >
> > > diff --git a/src/backend/utils/adt/numeric.c
> > > b/src/backend/utils/adt/numeric.c index a00db3ce7a..35234aee4c
> > > 100644 --- a/src/backend/utils/adt/numeric.c
> > > +++ b/src/backend/utils/adt/numeric.c
> > >
> > > ****
> > > I believe the hunks in this file should start at about line# 3181.
> > > This is right after numeric_scale().  Seems like all the scale
> > > related functions should be together.
> > >
> > > There's no hard standard but I don't see why lines (comment lines in
> > > your case) should be longer than 78 characters without good reason.
> > > Please reformat.
> > > ****
>
> I don't see any response from you regarding the above two suggestions.
>
>
> >
> > > + */
> > > +static int
> > > +get_min_scale(NumericVar *var)
> > > +{
> > > +       int             minscale = 0;
> > > +
> > > +       if (var->ndigits > 0)
> > > +       {
> > > +               NumericDigit last_digit;
> > > +
> > > +               /* maximal size of minscale, can be lower */
> > > +               minscale = (var->ndigits - var->weight - 1) *
> > >   DEC_DIGITS; +
> > > +               /*
> > > +                * When there are not digits after decimal point,
> > > the previous expression
> > >
> > > ****
> > > s/not/no/
> > > ****
> > >
> > > +                * can be negative. In this case, the minscale must
> > > be zero.
> > > +                */
> > >
> > > ****
> > > s/can be/is/
> > > ****
>
> By the above, I intended the comment be changed (after line wrapping)
> to:
>                /*
>                  * When there are no digits after decimal point,
>                  * the previous expression is negative. In this
>                  * case the minscale must be zero.
>                  */
>
> (Oh yes, on re-reading I think the comma is unnecessary so I removed it
> too.)
>
>
>
> > >
> > > +               if (minscale > 0)
> > > +               {
> > > +                       /* reduce minscale if trailing digits in
> > > last numeric digits are zero */
>
> And the above comment should either be wrapped (as requested above)
> or eliminated.  I like comments but I'm not sure this one contributes
> anything.
>
>
> > > --- a/src/include/catalog/pg_proc.dat
> > > +++ b/src/include/catalog/pg_proc.dat
> > > @@ -4288,6 +4288,12 @@
> > >    proname => 'width_bucket', prorettype => 'int4',
> > >    proargtypes => 'numeric numeric numeric int4',
> > >    prosrc => 'width_bucket_numeric' },
> > > +{ oid => '3434', descr => 'returns minimal scale of numeric value',
> > >
> > > ****
> > > How about a descr of?:
> > >
> > >   minimal scale needed to store the supplied value without data loss
> > > ****
> > >
> >
> > done
> >
> > >
> > > +  proname => 'minscale', prorettype => 'int4', proargtypes =>
> > >   'numeric',
> > > +  prosrc => 'numeric_minscale' },
> > > +{ oid => '3435', descr => 'returns numeric value with minimal
> > > scale',
> > >
> > > ****
> > > And likewise a descr of?:
> > >
> > >   numeric with minimal scale needed to represent the given value
> > > ****
> > >
> > > +  proname => 'trim_scale', prorettype => 'numeric', proargtypes =>
> > >   'numeric',
> > > +  prosrc => 'numeric_trim_scale' },
> > >
> >
> > done
>
> Thanks for these changes.  Looking at pg_proc.dat there seems to
> be an effort made to keep the lines to a maximum of 78 or 80
> characters.  This means starting "descr => '..." on new lines
> when the description is long.  Please reformat, doing this or,
> if you like, something even more clever to keep the lines short.
>
> Looking good.  We're making progress.
>

I fixed almost all mentioned issues (that I understand)

I am sending updated patch

Regards

Pavel

>
> Regards,
>
> Karl <k...@meme.com>
> Free Software:  "You don't pay back, you pay forward."
>                  -- Robert A. Heinlein
>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 57a1539506..2359478792 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -918,6 +918,20 @@
        <entry><literal>6.0000000000</literal></entry>
       </row>
 
+      <row>
+       <entry>
+        <indexterm>
+         <primary>min_scale</primary>
+        </indexterm>
+        <literal><function>min_scale(<type>numeric</type>)</function></literal>
+       </entry>
+       <entry><type>integer</type></entry>
+       <entry>minimal scale (number of decimal digits in the fractional part) needed
+       to store the supplied value without data loss</entry>
+       <entry><literal>min_scale(8.4100)</literal></entry>
+       <entry><literal>2</literal></entry>
+      </row>
+
       <row>
        <entry>
         <indexterm>
@@ -1041,6 +1055,20 @@
        <entry><literal>1.4142135623731</literal></entry>
       </row>
 
+      <row>
+       <entry>
+        <indexterm>
+         <primary>trim_scale</primary>
+        </indexterm>
+        <literal><function>trim_scale(<type>numeric</type>)</function></literal>
+       </entry>
+       <entry><type>numeric</type></entry>
+       <entry>reduce the scale (the number of decimal digits in the fractional part)
+       to the minimum needed to store the supplied value without data loss</entry>
+       <entry><literal>scale(8.4100)</literal></entry>
+       <entry><literal>8.41</literal></entry>
+      </row>
+
       <row>
        <entry>
         <indexterm>
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index a00db3ce7a..0095fb2f88 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -5620,6 +5620,92 @@ int2int4_sum(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(Int64GetDatumFast(transdata->sum));
 }
 
+/*
+ * Calculate minimal scale. The var should be stripped already.
+ */
+static int
+get_min_scale(NumericVar *var)
+{
+	int		minscale = 0;
+
+	if (var->ndigits > 0)
+	{
+		NumericDigit last_digit;
+
+		/* maximal size of minscale, can be lower */
+		minscale = (var->ndigits - var->weight - 1) * DEC_DIGITS;
+
+		/*
+		 * When there are no digits after decimal point,
+		 * the previous expression is negative. In this
+		 * case the minscale must be zero.
+		 */
+		if (minscale > 0)
+		{
+			/*
+			 * Reduce minscale if trailing digits in last numeric
+			 * digits are zero.
+			 */
+			last_digit = var->digits[var->ndigits - 1];
+
+			while (last_digit % 10 ==  0)
+			{
+				minscale--;
+				last_digit /= 10;
+			}
+		}
+		else
+			minscale = 0;
+	}
+
+	return minscale;
+}
+
+/*
+ * Returns minimal scale required to represent supplied value without loss.
+ */
+Datum
+numeric_min_scale(PG_FUNCTION_ARGS)
+{
+	Numeric		num = PG_GETARG_NUMERIC(0);
+	NumericVar	arg;
+	int			minscale;
+
+	if (NUMERIC_IS_NAN(num))
+		PG_RETURN_NULL();
+
+	init_var_from_num(num, &arg);
+	strip_var(&arg);
+
+	minscale = get_min_scale(&arg);
+	free_var(&arg);
+
+	PG_RETURN_INT32(minscale);
+}
+
+/*
+ * Reduce scale of numeric value to represent supplied value without loss.
+ */
+Datum
+numeric_trim_scale(PG_FUNCTION_ARGS)
+{
+	Numeric		num = PG_GETARG_NUMERIC(0);
+	Numeric		res;
+	NumericVar	result;
+
+	if (NUMERIC_IS_NAN(num))
+		PG_RETURN_NUMERIC(make_result(&const_nan));
+
+	init_var_from_num(num, &result);
+	strip_var(&result);
+
+	result.dscale = get_min_scale(&result);
+
+	res = make_result(&result);
+	free_var(&result);
+
+	PG_RETURN_NUMERIC(res);
+}
 
 /* ----------------------------------------------------------------------
  *
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index ac8f64b219..71a9387652 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -4254,6 +4254,14 @@
 { oid => '3281', descr => 'number of decimal digits in the fractional part',
   proname => 'scale', prorettype => 'int4', proargtypes => 'numeric',
   prosrc => 'numeric_scale' },
+{ oid => '3434',
+  descr => 'minimal scale needed to store the supplied value without data loss',
+  proname => 'min_scale', prorettype => 'int4', proargtypes => 'numeric',
+  prosrc => 'numeric_min_scale' },
+{ oid => '3435',
+  descr => 'numeric with minimal scale needed to represent the given value',
+  proname => 'trim_scale', prorettype => 'numeric', proargtypes => 'numeric',
+  prosrc => 'numeric_trim_scale' },
 { oid => '1740', descr => 'convert int4 to numeric',
   proname => 'numeric', prorettype => 'numeric', proargtypes => 'int4',
   prosrc => 'int4_numeric' },
diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out
index 1cb3c3bfab..91fc90b80d 100644
--- a/src/test/regress/expected/numeric.out
+++ b/src/test/regress/expected/numeric.out
@@ -2078,6 +2078,192 @@ select scale(-13.000000000000000);
     15
 (1 row)
 
+--
+-- Tests for min_scale()
+--
+select min_scale(numeric 'NaN') is NULL; -- should be true
+ ?column? 
+----------
+ t
+(1 row)
+
+select min_scale(NULL::numeric) is NULL; -- should be true
+ ?column? 
+----------
+ t
+(1 row)
+
+select min_scale(0);                     -- no digits
+ min_scale 
+-----------
+         0
+(1 row)
+
+select min_scale(0.00);                  -- no digits again
+ min_scale 
+-----------
+         0
+(1 row)
+
+select min_scale(1.0);                   -- no scale
+ min_scale 
+-----------
+         0
+(1 row)
+
+select min_scale(1.1);                   -- scale 1
+ min_scale 
+-----------
+         1
+(1 row)
+
+select min_scale(1.12);                  -- scale 2
+ min_scale 
+-----------
+         2
+(1 row)
+
+select min_scale(1.123);                 -- scale 3
+ min_scale 
+-----------
+         3
+(1 row)
+
+select min_scale(1.1234);                -- scale 4, filled digit
+ min_scale 
+-----------
+         4
+(1 row)
+
+select min_scale(1.12345);               -- scale 5, 2 NDIGITS
+ min_scale 
+-----------
+         5
+(1 row)
+
+select min_scale(1.1000);                -- 1 pos in NDIGITS
+ min_scale 
+-----------
+         1
+(1 row)
+
+select min_scale(1.1200);                -- 2 pos in NDIGITS
+ min_scale 
+-----------
+         2
+(1 row)
+
+select min_scale(1.1230);                -- 3 pos in NDIGITS
+ min_scale 
+-----------
+         3
+(1 row)
+
+select min_scale(1.1234);                -- all pos in NDIGITS
+ min_scale 
+-----------
+         4
+(1 row)
+
+select min_scale(1.12345000);            -- 2 NDIGITS
+ min_scale 
+-----------
+         5
+(1 row)
+
+select min_scale(1.123400000000);        -- strip() required/done
+ min_scale 
+-----------
+         4
+(1 row)
+
+select min_scale(12345.123456789012345); -- "big" number
+ min_scale 
+-----------
+        15
+(1 row)
+
+select min_scale(-12345.12345);          -- negative number
+ min_scale 
+-----------
+         5
+(1 row)
+
+select min_scale(1e100);                 -- very big number
+ min_scale 
+-----------
+         0
+(1 row)
+
+select min_scale(1e100::numeric + 0.1);  -- big number with scale
+ min_scale 
+-----------
+         1
+(1 row)
+
+--
+-- Tests for trim_scale()
+--
+select trim_scale(numeric 'NaN') = 'NaN':: numeric; -- should be true
+ ?column? 
+----------
+ t
+(1 row)
+
+select trim_scale(NULL::numeric) IS NULL; -- should be true
+ ?column? 
+----------
+ t
+(1 row)
+
+select trim_scale(1.120);
+ trim_scale 
+------------
+       1.12
+(1 row)
+
+select trim_scale(0);
+ trim_scale 
+------------
+          0
+(1 row)
+
+select trim_scale(0.00);
+ trim_scale 
+------------
+          0
+(1 row)
+
+select trim_scale(1.1234500);
+ trim_scale 
+------------
+    1.12345
+(1 row)
+
+select trim_scale(110123.12475871856128000);
+      trim_scale       
+-----------------------
+ 110123.12475871856128
+(1 row)
+
+select trim_scale(-1123.124718561280000000);
+    trim_scale     
+-------------------
+ -1123.12471856128
+(1 row)
+
+select trim_scale(-13.00000000000000000000);
+ trim_scale 
+------------
+        -13
+(1 row)
+
+select trim_scale(1e100);
+                                              trim_scale                                               
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
 --
 -- Tests for SUM()
 --
diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql
index a939412359..c4fb23df3e 100644
--- a/src/test/regress/sql/numeric.sql
+++ b/src/test/regress/sql/numeric.sql
@@ -1036,6 +1036,46 @@ select scale(110123.12475871856128);
 select scale(-1123.12471856128);
 select scale(-13.000000000000000);
 
+--
+-- Tests for min_scale()
+--
+
+select min_scale(numeric 'NaN') is NULL; -- should be true
+select min_scale(NULL::numeric) is NULL; -- should be true
+select min_scale(0);                     -- no digits
+select min_scale(0.00);                  -- no digits again
+select min_scale(1.0);                   -- no scale
+select min_scale(1.1);                   -- scale 1
+select min_scale(1.12);                  -- scale 2
+select min_scale(1.123);                 -- scale 3
+select min_scale(1.1234);                -- scale 4, filled digit
+select min_scale(1.12345);               -- scale 5, 2 NDIGITS
+select min_scale(1.1000);                -- 1 pos in NDIGITS
+select min_scale(1.1200);                -- 2 pos in NDIGITS
+select min_scale(1.1230);                -- 3 pos in NDIGITS
+select min_scale(1.1234);                -- all pos in NDIGITS
+select min_scale(1.12345000);            -- 2 NDIGITS
+select min_scale(1.123400000000);        -- strip() required/done
+select min_scale(12345.123456789012345); -- "big" number
+select min_scale(-12345.12345);          -- negative number
+select min_scale(1e100);                 -- very big number
+select min_scale(1e100::numeric + 0.1);  -- big number with scale
+
+--
+-- Tests for trim_scale()
+--
+
+select trim_scale(numeric 'NaN') = 'NaN':: numeric; -- should be true
+select trim_scale(NULL::numeric) IS NULL; -- should be true
+select trim_scale(1.120);
+select trim_scale(0);
+select trim_scale(0.00);
+select trim_scale(1.1234500);
+select trim_scale(110123.12475871856128000);
+select trim_scale(-1123.124718561280000000);
+select trim_scale(-13.00000000000000000000);
+select trim_scale(1e100);
+
 --
 -- Tests for SUM()
 --

Reply via email to