Skip to content

Commit a0fecf9

Browse files
author
kinash-varvara
authored
Merge 73ae889 into 1717439
2 parents 1717439 + 73ae889 commit a0fecf9

File tree

2 files changed

+104
-26
lines changed

2 files changed

+104
-26
lines changed

ydb/library/yql/parser/pg_wrapper/postgresql/src/backend/utils/adt/numeric.c

+54-26
Original file line numberDiff line numberDiff line change
@@ -4089,58 +4089,86 @@ int64_to_numeric(int64 val)
40894089
}
40904090

40914091
/*
4092-
* Convert val1/(10**val2) to numeric. This is much faster than normal
4092+
* Convert val1/(10**log10val2) to numeric. This is much faster than normal
40934093
* numeric division.
40944094
*/
40954095
Numeric
40964096
int64_div_fast_to_numeric(int64 val1, int log10val2)
40974097
{
40984098
Numeric res;
40994099
NumericVar result;
4100-
int64 saved_val1 = val1;
4100+
int rscale;
41014101
int w;
41024102
int m;
41034103

4104+
init_var(&result);
4105+
4106+
/* result scale */
4107+
rscale = log10val2 < 0 ? 0 : log10val2;
4108+
41044109
/* how much to decrease the weight by */
41054110
w = log10val2 / DEC_DIGITS;
4106-
/* how much is left */
4111+
/* how much is left to divide by */
41074112
m = log10val2 % DEC_DIGITS;
4113+
if (m < 0)
4114+
{
4115+
m += DEC_DIGITS;
4116+
w--;
4117+
}
41084118

41094119
/*
4110-
* If there is anything left, multiply the dividend by what's left, then
4111-
* shift the weight by one more.
4120+
* If there is anything left to divide by (10^m with 0 < m < DEC_DIGITS),
4121+
* multiply the dividend by 10^(DEC_DIGITS - m), and shift the weight by
4122+
* one more.
41124123
*/
41134124
if (m > 0)
41144125
{
4115-
static __thread int pow10[] = {1, 10, 100, 1000};
4126+
#if DEC_DIGITS == 4
4127+
static __thread int pow10[] = {1, 10, 100, 1000};
4128+
#elif DEC_DIGITS == 2
4129+
static __thread int pow10[] = {1, 10};
4130+
#elif DEC_DIGITS == 1
4131+
static __thread int pow10[] = {1};
4132+
#else
4133+
#error unsupported NBASE
4134+
#endif
4135+
int64 factor = pow10[DEC_DIGITS - m];
4136+
int64 new_val1;
41164137

41174138
StaticAssertStmt(lengthof(pow10) == DEC_DIGITS, "mismatch with DEC_DIGITS");
4118-
if (unlikely(pg_mul_s64_overflow(val1, pow10[DEC_DIGITS - m], &val1)))
4139+
4140+
if (unlikely(pg_mul_s64_overflow(val1, factor, &new_val1)))
41194141
{
4120-
/*
4121-
* If it doesn't fit, do the whole computation in numeric the slow
4122-
* way. Note that va1l may have been overwritten, so use
4123-
* saved_val1 instead.
4124-
*/
4125-
int val2 = 1;
4126-
4127-
for (int i = 0; i < log10val2; i++)
4128-
val2 *= 10;
4129-
res = numeric_div_opt_error(int64_to_numeric(saved_val1), int64_to_numeric(val2), NULL);
4130-
res = DatumGetNumeric(DirectFunctionCall2(numeric_round,
4131-
NumericGetDatum(res),
4132-
Int32GetDatum(log10val2)));
4133-
return res;
4142+
#ifdef HAVE_INT128
4143+
/* do the multiplication using 128-bit integers */
4144+
int128 tmp;
4145+
4146+
tmp = (int128) val1 * (int128) factor;
4147+
4148+
int128_to_numericvar(tmp, &result);
4149+
#else
4150+
/* do the multiplication using numerics */
4151+
NumericVar tmp;
4152+
4153+
init_var(&tmp);
4154+
4155+
int64_to_numericvar(val1, &result);
4156+
int64_to_numericvar(factor, &tmp);
4157+
mul_var(&result, &tmp, &result, 0);
4158+
4159+
free_var(&tmp);
4160+
#endif
41344161
}
4162+
else
4163+
int64_to_numericvar(new_val1, &result);
4164+
41354165
w++;
41364166
}
4137-
4138-
init_var(&result);
4139-
4140-
int64_to_numericvar(val1, &result);
4167+
else
4168+
int64_to_numericvar(val1, &result);
41414169

41424170
result.weight -= w;
4143-
result.dscale += w * DEC_DIGITS - (DEC_DIGITS - m);
4171+
result.dscale = rscale;
41444172

41454173
res = make_result(&result);
41464174

ydb/library/yql/parser/pg_wrapper/ut/arrow_ut.cpp

+50
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,31 @@ Y_UNIT_TEST(PgConvertNumericDecimal128Scale1) {
110110
checkResult<false>(expected, result, &reader, numeric_out);
111111
}
112112

113+
Y_UNIT_TEST(PgConvertNumericDecimal128ScaleNegative) {
114+
TArenaMemoryContext arena;
115+
116+
int32_t precision = 8;
117+
int32_t scale = -3;
118+
std::shared_ptr<arrow::DataType> type(new arrow::Decimal128Type(precision, scale));
119+
arrow::Decimal128Builder builder(type);
120+
121+
const char* expected[] = {
122+
"12345678000", "-12345678000", nullptr
123+
};
124+
125+
ARROW_OK(builder.Append(arrow::Decimal128::FromString("12345678").ValueOrDie()));
126+
ARROW_OK(builder.Append(arrow::Decimal128::FromString("-12345678").ValueOrDie()));
127+
ARROW_OK(builder.AppendNull());
128+
129+
std::shared_ptr<arrow::Array> array;
130+
ARROW_OK(builder.Finish(&array));
131+
132+
auto result = PgDecimal128ConvertNumeric(array, precision, scale);
133+
134+
NYql::NUdf::TStringBlockReader<arrow::BinaryType, true> reader;
135+
checkResult<false>(expected, result, &reader, numeric_out);
136+
}
137+
113138
Y_UNIT_TEST(PgConvertNumericDecimal128Scale2) {
114139
TArenaMemoryContext arena;
115140

@@ -160,6 +185,31 @@ Y_UNIT_TEST(PgConvertNumericDecimal128Scale3) {
160185
checkResult<false>(expected, result, &reader, numeric_out);
161186
}
162187

188+
Y_UNIT_TEST(PgConvertNumericDecimal128Scale4) {
189+
TArenaMemoryContext arena;
190+
191+
int32_t precision = 7;
192+
int32_t scale = 4;
193+
std::shared_ptr<arrow::DataType> type(new arrow::Decimal128Type(precision, scale));
194+
arrow::Decimal128Builder builder(type);
195+
196+
const char* expected[] = {
197+
"123.4567", "-123.4567", nullptr
198+
};
199+
200+
ARROW_OK(builder.Append(arrow::Decimal128::FromString("123.4567").ValueOrDie()));
201+
ARROW_OK(builder.Append(arrow::Decimal128::FromString("-123.4567").ValueOrDie()));
202+
ARROW_OK(builder.AppendNull());
203+
204+
std::shared_ptr<arrow::Array> array;
205+
ARROW_OK(builder.Finish(&array));
206+
207+
auto result = PgDecimal128ConvertNumeric(array, precision, scale);
208+
209+
NYql::NUdf::TStringBlockReader<arrow::BinaryType, true> reader;
210+
checkResult<false>(expected, result, &reader, numeric_out);
211+
}
212+
163213
Y_UNIT_TEST(PgConvertNumericDecimal128Scale5) {
164214
TArenaMemoryContext arena;
165215

0 commit comments

Comments
 (0)