Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b2d4792

Browse filesBrowse files
committed
Make int64_div_fast_to_numeric() more robust.
The prior coding of int64_div_fast_to_numeric() had a number of bugs that would cause it to fail under different circumstances, such as with log10val2 <= 0, or log10val2 a multiple of 4, or in the "slow" numeric path with log10val2 >= 10. None of those could be triggered by any of our current code, which only uses log10val2 = 3 or 6. However, they made it a hazard for any future code that might use it. Also, since this is exported by numeric.c, users writing their own C code might choose to use it. Therefore fix, and back-patch to v14, where it was introduced. Dean Rasheed, reviewed by Tom Lane. Discussion: https://postgr.es/m/CAEZATCW8gXgW0tgPxPgHDPhVX71%2BSWFRkhnXy%2BTfGDsKLepu2g%40mail.gmail.com
1 parent 2010d8b commit b2d4792
Copy full SHA for b2d4792

File tree

1 file changed

+46
-27
lines changed
Filter options

1 file changed

+46
-27
lines changed

‎src/backend/utils/adt/numeric.c

Copy file name to clipboardExpand all lines: src/backend/utils/adt/numeric.c
+46-27Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4235,67 +4235,86 @@ int64_to_numeric(int64 val)
42354235
}
42364236

42374237
/*
4238-
* Convert val1/(10**val2) to numeric. This is much faster than normal
4238+
* Convert val1/(10**log10val2) to numeric. This is much faster than normal
42394239
* numeric division.
42404240
*/
42414241
Numeric
42424242
int64_div_fast_to_numeric(int64 val1, int log10val2)
42434243
{
42444244
Numeric res;
42454245
NumericVar result;
4246-
int64 saved_val1 = val1;
4246+
int rscale;
42474247
int w;
42484248
int m;
42494249

4250+
init_var(&result);
4251+
4252+
/* result scale */
4253+
rscale = log10val2 < 0 ? 0 : log10val2;
4254+
42504255
/* how much to decrease the weight by */
42514256
w = log10val2 / DEC_DIGITS;
4252-
/* how much is left */
4257+
/* how much is left to divide by */
42534258
m = log10val2 % DEC_DIGITS;
4259+
if (m < 0)
4260+
{
4261+
m += DEC_DIGITS;
4262+
w--;
4263+
}
42544264

42554265
/*
4256-
* If there is anything left, multiply the dividend by what's left, then
4257-
* shift the weight by one more.
4266+
* If there is anything left to divide by (10^m with 0 < m < DEC_DIGITS),
4267+
* multiply the dividend by 10^(DEC_DIGITS - m), and shift the weight by
4268+
* one more.
42584269
*/
42594270
if (m > 0)
42604271
{
42614272
#if DEC_DIGITS == 4
4262-
static int pow10[] = {1, 10, 100, 1000};
4273+
static const int pow10[] = {1, 10, 100, 1000};
42634274
#elif DEC_DIGITS == 2
4264-
static int pow10[] = {1, 10};
4275+
static const int pow10[] = {1, 10};
42654276
#elif DEC_DIGITS == 1
4266-
static int pow10[] = {1};
4277+
static const int pow10[] = {1};
42674278
#else
42684279
#error unsupported NBASE
42694280
#endif
4281+
int64 factor = pow10[DEC_DIGITS - m];
4282+
int64 new_val1;
42704283

42714284
StaticAssertDecl(lengthof(pow10) == DEC_DIGITS, "mismatch with DEC_DIGITS");
42724285

4273-
if (unlikely(pg_mul_s64_overflow(val1, pow10[DEC_DIGITS - m], &val1)))
4286+
if (unlikely(pg_mul_s64_overflow(val1, factor, &new_val1)))
42744287
{
4275-
/*
4276-
* If it doesn't fit, do the whole computation in numeric the slow
4277-
* way. Note that va1l may have been overwritten, so use
4278-
* saved_val1 instead.
4279-
*/
4280-
int val2 = 1;
4288+
#ifdef HAVE_INT128
4289+
/* do the multiplication using 128-bit integers */
4290+
int128 tmp;
42814291

4282-
for (int i = 0; i < log10val2; i++)
4283-
val2 *= 10;
4284-
res = numeric_div_opt_error(int64_to_numeric(saved_val1), int64_to_numeric(val2), NULL);
4285-
res = DatumGetNumeric(DirectFunctionCall2(numeric_round,
4286-
NumericGetDatum(res),
4287-
Int32GetDatum(log10val2)));
4288-
return res;
4292+
tmp = (int128) val1 * (int128) factor;
4293+
4294+
int128_to_numericvar(tmp, &result);
4295+
#else
4296+
/* do the multiplication using numerics */
4297+
NumericVar tmp;
4298+
4299+
init_var(&tmp);
4300+
4301+
int64_to_numericvar(val1, &result);
4302+
int64_to_numericvar(factor, &tmp);
4303+
mul_var(&result, &tmp, &result, 0);
4304+
4305+
free_var(&tmp);
4306+
#endif
42894307
}
4308+
else
4309+
int64_to_numericvar(new_val1, &result);
4310+
42904311
w++;
42914312
}
4292-
4293-
init_var(&result);
4294-
4295-
int64_to_numericvar(val1, &result);
4313+
else
4314+
int64_to_numericvar(val1, &result);
42964315

42974316
result.weight -= w;
4298-
result.dscale += w * DEC_DIGITS - (DEC_DIGITS - m);
4317+
result.dscale = rscale;
42994318

43004319
res = make_result(&result);
43014320

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.