Skip to content

Commit 2b1c9b2

Browse files
authored
Handle T_OpExpr for fixed length datatypes with other operand as numeric (#3821) (#3861)
This PR fixes typmod handling for queries where sub-expressions have a resultant type of fixed-size datatypes (money, smallmoney,int,bigint,smallint,tinyint) and the overall query have arithmetic operation involves numeric datatype. Previously in babelfish, this either leads to producing incorrect results, or throw "Arithmetic overflow error". This PR ensures correct precision/scale calculations for fixed sized datatypes in T_OpExpr node. Signed-off-by: Tanya Gupta tanyagp@amazon.com Issues Resolved BABEL-5899
1 parent 4f7dfc3 commit 2b1c9b2

File tree

13 files changed

+2820
-112
lines changed

13 files changed

+2820
-112
lines changed

contrib/babelfishpg_tds/src/backend/tds/tds_srv.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ pe_tds_init(void)
205205
pltsql_plugin_handler_ptr->get_datum_from_date_time_struct = &TdsDateTimeTypeToDatum;
206206
pltsql_plugin_handler_ptr->set_reset_tds_connection_flag = &SetResetTDSConnectionFlag;
207207
pltsql_plugin_handler_ptr->get_reset_tds_connection_flag = &GetResetTDSConnectionFlag;
208-
pltsql_plugin_handler_ptr->get_tds_numeric_get_typmod = &tds_numeric_get_typmod;
209208

210209
invalidate_stat_table_hook = invalidate_stat_table;
211210
guc_newval_hook = TdsSetGucStatVariable;

contrib/babelfishpg_tds/src/backend/utils/adt/numeric.c

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -749,44 +749,3 @@ TdsSetVarFromStrWrapper(const char *str)
749749
return res;
750750
}
751751

752-
/*
753-
* Fix Me: It is a duplicate function of tsql_numeric_get_typmod
754-
* Get Precision & Scale from Numeric Value
755-
*/
756-
int32_t
757-
tds_numeric_get_typmod(Numeric num)
758-
{
759-
int32_t scale = NUMERIC_DSCALE(num);
760-
int32_t weight = NUMERIC_WEIGHT(num);
761-
int32_t precision;
762-
763-
if (weight >= 0 && NUMERIC_NDIGITS(num) != 0)
764-
{
765-
static const int32 timescales[DEC_DIGITS] = {
766-
1000,
767-
100,
768-
10,
769-
1,
770-
};
771-
int leading_digits = NUMERIC_DIGITS(num)[0];
772-
773-
precision = weight * DEC_DIGITS + scale;
774-
775-
for (int i = 0; i < DEC_DIGITS; i++)
776-
{
777-
if (leading_digits >= timescales[i])
778-
{
779-
precision += (4 - i);
780-
break;
781-
}
782-
}
783-
}
784-
else if (NUMERIC_NDIGITS(num) == 0 && scale == 0)
785-
/* NUMERIC_NDIGITS(num) == 0 && scale == 0 means number is 0 */
786-
precision = 1;
787-
else
788-
/* weight < 0 means the integral part of the number is 0 */
789-
precision = scale;
790-
791-
return (((precision & 0xFFFF) << 16) | (scale & 0xFFFF)) + VARHDRSZ;
792-
}

contrib/babelfishpg_tds/src/include/tds_int.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,6 @@ extern void pe_fin(void);
369369

370370
/* Functions in backend/utils/adt/numeric.c */
371371
extern Numeric TdsSetVarFromStrWrapper(const char *str);
372-
extern int32_t tds_numeric_get_typmod(Numeric num);
373372

374373
/* Functions in backend/utils/adt/varchar.c */
375374
extern void *tds_varchar_input(const char *s, size_t len, int32 atttypmod);

contrib/babelfishpg_tsql/src/pltsql.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1808,7 +1808,6 @@ typedef struct PLtsql_protocol_plugin
18081808
bool (*get_reset_tds_connection_flag) ();
18091809
void (*get_tvp_typename_typeschemaname) (char *proc_name, char *target_arg_name,
18101810
char **tvp_type_name, char **tvp_type_schema_name);
1811-
int32_t (*get_tds_numeric_get_typmod) (Numeric num);
18121811

18131812
/* Session level GUCs */
18141813
bool quoted_identifier;

contrib/babelfishpg_tsql/src/pltsql_coerce.c

Lines changed: 79 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,12 @@ static bool is_tsql_numeric_fixeddecimal(Oid oid);
8282
static bool is_tsql_bit_numeric(Oid oid);
8383
static bool is_tsql_int4_bit(Oid oid);
8484

85-
#define SMALLINT_PRECISION_RADIX 5
86-
#define INT_PRECISION_RADIX 10
87-
#define BIGINT_PRECISION_RADIX 19
88-
#define TINYINT_PRECISION_RADIX 3
85+
#define TINYINT_PRECISION_RADIX 3
86+
#define SMALLINT_PRECISION_RADIX 5
87+
#define INT_PRECISION_RADIX 10
88+
#define BIGINT_PRECISION_RADIX 19
8989

90+
#define DEFAULT_TINYINT_TYPMOD ((TINYINT_PRECISION_RADIX << 16) | 0) + VARHDRSZ
9091
#define DEFAULT_SMALLINT_TYPMOD ((SMALLINT_PRECISION_RADIX << 16) | 0) + VARHDRSZ
9192
#define DEFAULT_INT_TYPMOD ((INT_PRECISION_RADIX << 16) | 0) + VARHDRSZ
9293
#define DEFAULT_BIGINT_TYPMOD ((BIGINT_PRECISION_RADIX << 16) | 0) + VARHDRSZ
@@ -1205,15 +1206,43 @@ is_numeric_datatype(Oid typid)
12051206
return false;
12061207
}
12071208

1209+
/*
1210+
* get_default_typmod_for_fixedsize_dataypes
1211+
*
1212+
* Assigns predefined typmod values for fixed-length datatypes
1213+
* (int, bigint, smallint, tinyint) and money/smallmoney.
1214+
* These typmods represent the maximum allowed precision for each type
1215+
* and are used during typmod resolution to ensure consistent handling
1216+
* in expressions involving fixed-size numeric types.
1217+
*/
1218+
static int32
1219+
get_default_typmod_for_fixedsize_dataypes(Oid resulttype)
1220+
{
1221+
if (resulttype == INT4OID)
1222+
return DEFAULT_INT_TYPMOD;
1223+
else if (resulttype == INT8OID)
1224+
return DEFAULT_BIGINT_TYPMOD;
1225+
else if (resulttype == INT2OID)
1226+
return DEFAULT_SMALLINT_TYPMOD;
1227+
else if ((*common_utility_plugin_ptr->is_tsql_tinyint_datatype)(resulttype))
1228+
return DEFAULT_TINYINT_TYPMOD;
1229+
else if ((*common_utility_plugin_ptr->is_tsql_money_datatype)(resulttype))
1230+
return TSQL_MONEY_TYPMOD;
1231+
else if ((*common_utility_plugin_ptr->is_tsql_smallmoney_datatype)(resulttype))
1232+
return TSQL_SMALLMONEY_TYPMOD;
1233+
1234+
return -1;
1235+
}
1236+
12081237
/*
1209-
* look for a typmod to return from a numeric expression,
1210-
* Also for cases where we cannot compute the expression typmod return -1 and set found as false.
1238+
* Look for a typmod to return from a numeric expression,
1239+
* also for cases where we cannot compute the expression typmod return -1 and set found as false.
12111240
*/
12121241
int32
12131242
resolve_numeric_typmod_from_exp(Plan *plan, Node *expr, bool *found)
12141243
{
12151244
/*
1216-
* set found value as true by default, if we are unable to
1245+
* Set found value as true by default, if we are unable to
12171246
* find the expression typmod found will be set to false.
12181247
*/
12191248
if (found != NULL)
@@ -1231,6 +1260,7 @@ resolve_numeric_typmod_from_exp(Plan *plan, Node *expr, bool *found)
12311260
Param *param = (Param *) expr;
12321261
if (param->paramtypmod == -1)
12331262
{
1263+
int32 fixlen_default_typmod;
12341264
/* UDT handling in T_Param */
12351265
Oid immediate_base_type = get_immediate_base_type_of_UDT_internal(param->paramtype);
12361266
if (OidIsValid(immediate_base_type))
@@ -1241,15 +1271,14 @@ resolve_numeric_typmod_from_exp(Plan *plan, Node *expr, bool *found)
12411271
return typmod;
12421272
}
12431273

1244-
/* handling for fixed length datatypes */
1245-
if (param->paramtype == INT4OID)
1246-
return DEFAULT_INT_TYPMOD;
1247-
else if (param->paramtype == INT8OID)
1248-
return DEFAULT_BIGINT_TYPMOD;
1249-
else if (param->paramtype == INT2OID)
1250-
return DEFAULT_SMALLINT_TYPMOD;
1251-
else if ((*common_utility_plugin_ptr->is_tsql_tinyint_datatype) (param->paramtype))
1252-
return DEFAULT_TINYINT_TYPMOD;
1274+
/*
1275+
* Handle default typmod for supported fixed-length datatypes
1276+
* such as bigint, int, smallint, and tinyint.
1277+
* These typmods represent the maximum allowed digits for each type.
1278+
*/
1279+
fixlen_default_typmod = get_default_typmod_for_fixedsize_dataypes(param->paramtype);
1280+
if (fixlen_default_typmod != -1)
1281+
return fixlen_default_typmod;
12531282
}
12541283

12551284
if (!is_numeric_datatype(param->paramtype) &&
@@ -1302,17 +1331,20 @@ resolve_numeric_typmod_from_exp(Plan *plan, Node *expr, bool *found)
13021331
{
13031332
val = con->constvalue;
13041333
num = int64_to_numeric(val);
1305-
if (*pltsql_protocol_plugin_ptr && (*pltsql_protocol_plugin_ptr)->get_tds_numeric_get_typmod)
1306-
return (*pltsql_protocol_plugin_ptr)->get_tds_numeric_get_typmod(num);
1334+
if ((*common_utility_plugin_ptr->tsql_numeric_get_typmod))
1335+
{
1336+
return ((*common_utility_plugin_ptr->tsql_numeric_get_typmod)(num));
1337+
}
13071338

13081339
if (found != NULL) *found = false;
13091340
return -1;
13101341
}
13111342

13121343
num = (Numeric) con->constvalue;
1313-
if (*pltsql_protocol_plugin_ptr && (*pltsql_protocol_plugin_ptr)->get_tds_numeric_get_typmod)
1314-
return (*pltsql_protocol_plugin_ptr)->get_tds_numeric_get_typmod(num);
1315-
1344+
if ((*common_utility_plugin_ptr->tsql_numeric_get_typmod))
1345+
{
1346+
return ((*common_utility_plugin_ptr->tsql_numeric_get_typmod)(num));
1347+
}
13161348
if (found != NULL) *found = false;
13171349
return -1;
13181350
}
@@ -1416,15 +1448,21 @@ resolve_numeric_typmod_from_exp(Plan *plan, Node *expr, bool *found)
14161448
return typmod;
14171449
}
14181450

1419-
/* handling for fixed length datatypes */
1420-
if (plan && var->vartype == INT4OID)
1421-
return DEFAULT_INT_TYPMOD;
1422-
else if (plan && var->vartype == INT8OID)
1423-
return DEFAULT_BIGINT_TYPMOD;
1424-
else if (plan && var->vartype == INT2OID)
1425-
return DEFAULT_SMALLINT_TYPMOD;
1426-
else if (plan && (*common_utility_plugin_ptr->is_tsql_tinyint_datatype) (var->vartype))
1427-
return DEFAULT_TINYINT_TYPMOD;
1451+
/*
1452+
* Handle default typmod for supported fixed-length datatypes
1453+
* such as bigint, int, smallint, and tinyint.
1454+
* These typmods represent the maximum allowed digits for each type.
1455+
*
1456+
* Plan check ensures typmod consistency to preventing incorrect values,
1457+
* ensuring plan is not changed if typmod is calculated in execution stage.
1458+
*/
1459+
if (plan)
1460+
{
1461+
int32 fixlen_default_typmod;
1462+
fixlen_default_typmod = get_default_typmod_for_fixedsize_dataypes(var->vartype);
1463+
if (fixlen_default_typmod != -1)
1464+
return fixlen_default_typmod;
1465+
}
14281466

14291467
if (found != NULL) *found = false;
14301468
}
@@ -1447,8 +1485,19 @@ resolve_numeric_typmod_from_exp(Plan *plan, Node *expr, bool *found)
14471485
precision;
14481486
uint8_t integralDigitCount = 0;
14491487
bool found_typmod;
1488+
int32 fixsize_default_typmod;
14501489

14511490
Assert(list_length(op->args) == 2 || list_length(op->args) == 1);
1491+
1492+
/*
1493+
* Handle default typmod for supported fixed-size datatypes
1494+
* such as money, smallmoney, bigint, int, smallint, and tinyint.
1495+
* These typmods represent the maximum allowed digits for each type.
1496+
*/
1497+
fixsize_default_typmod = get_default_typmod_for_fixedsize_dataypes(op->opresulttype);
1498+
if (fixsize_default_typmod != -1)
1499+
return fixsize_default_typmod;
1500+
14521501
if (list_length(op->args) == 2)
14531502
{
14541503
arg1 = linitial(op->args);

test/JDBC/expected/Numeric_Decimal_tests.out

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,7 +1428,7 @@ SELECT CAST(123.45 AS NUMERIC(5,2)) * CAST(678.90 AS NUMERIC(5,2)) / (CAST(2 AS
14281428
GO
14291429
~~START~~
14301430
numeric
1431-
16762.041000000000
1431+
16762.041000000000000
14321432
~~END~~
14331433

14341434

@@ -2098,7 +2098,7 @@ SELECT (@num1 * @num2) / (@int1 + @int2) AS result;
20982098
GO
20992099
~~START~~
21002100
numeric
2101-
16762.0410000000000000
2101+
16762.041000000000000
21022102
~~END~~
21032103

21042104

test/JDBC/expected/TestExactNumeric.out

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -229,32 +229,32 @@ SELECT
229229
CEILING(smallint_col + 0.5) AS ceil_smallint,
230230
CEILING(integer_col + 0.5) AS ceil_int,
231231
CEILING(bigint_col + 0.5) AS ceil_bigint
232-
FROM exactnumeric_table;
232+
FROM exactnumeric_table order by ceil_tinyint;
233233
GO
234234
~~START~~
235235
numeric#!#numeric#!#numeric#!#numeric
236-
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
237-
<NULL>#!#-32767.00000000#!#<NULL>#!#<NULL>
238236
<NULL>#!#<NULL>#!#-2147483647.00000000#!#<NULL>
237+
<NULL>#!#1.00000000#!#<NULL>#!#<NULL>
238+
<NULL>#!#<NULL>#!#1.00000000#!#<NULL>
239239
<NULL>#!#<NULL>#!#<NULL>#!#-9223372036854775807.00000000
240-
128.00000000#!#<NULL>#!#<NULL>#!#<NULL>
240+
<NULL>#!#<NULL>#!#<NULL>#!#1.00000000
241241
<NULL>#!#32768.00000000#!#<NULL>#!#<NULL>
242242
<NULL>#!#<NULL>#!#2147483648.00000000#!#<NULL>
243243
<NULL>#!#<NULL>#!#<NULL>#!#9223372036854775808.00000000
244244
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
245245
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
246246
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
247247
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
248-
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
248+
<NULL>#!#-32767.00000000#!#<NULL>#!#<NULL>
249249
<NULL>#!#1.00000000#!#<NULL>#!#<NULL>
250250
<NULL>#!#<NULL>#!#1.00000000#!#<NULL>
251251
<NULL>#!#<NULL>#!#<NULL>#!#1.00000000
252252
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
253-
<NULL>#!#1.00000000#!#<NULL>#!#<NULL>
254-
<NULL>#!#<NULL>#!#1.00000000#!#<NULL>
255-
<NULL>#!#<NULL>#!#<NULL>#!#1.00000000
256-
129.00000000#!#<NULL>#!#<NULL>#!#<NULL>
253+
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
254+
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
257255
6.00000000#!#101.00000000#!#10001.00000000#!#1000000001.00000000
256+
128.00000000#!#<NULL>#!#<NULL>#!#<NULL>
257+
129.00000000#!#<NULL>#!#<NULL>#!#<NULL>
258258
~~END~~
259259

260260

@@ -263,32 +263,32 @@ SELECT
263263
FLOOR(smallint_col + 0.5) AS floor_smallint,
264264
FLOOR(integer_col + 0.5) AS floor_int,
265265
FLOOR(bigint_col + 0.5) AS floor_bigint
266-
FROM exactnumeric_table;
266+
FROM exactnumeric_table order by floor_tinyint;
267267
GO
268268
~~START~~
269269
numeric#!#numeric#!#numeric#!#numeric
270-
0E-8#!#<NULL>#!#<NULL>#!#<NULL>
271-
<NULL>#!#-32768.00000000#!#<NULL>#!#<NULL>
272270
<NULL>#!#<NULL>#!#-2147483648.00000000#!#<NULL>
271+
<NULL>#!#0E-8#!#<NULL>#!#<NULL>
272+
<NULL>#!#<NULL>#!#0E-8#!#<NULL>
273273
<NULL>#!#<NULL>#!#<NULL>#!#-9223372036854775808.00000000
274-
127.00000000#!#<NULL>#!#<NULL>#!#<NULL>
274+
<NULL>#!#<NULL>#!#<NULL>#!#0E-8
275275
<NULL>#!#32767.00000000#!#<NULL>#!#<NULL>
276276
<NULL>#!#<NULL>#!#2147483647.00000000#!#<NULL>
277277
<NULL>#!#<NULL>#!#<NULL>#!#9223372036854775807.00000000
278278
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
279279
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
280280
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
281281
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
282-
0E-8#!#<NULL>#!#<NULL>#!#<NULL>
282+
<NULL>#!#-32768.00000000#!#<NULL>#!#<NULL>
283283
<NULL>#!#0E-8#!#<NULL>#!#<NULL>
284284
<NULL>#!#<NULL>#!#0E-8#!#<NULL>
285285
<NULL>#!#<NULL>#!#<NULL>#!#0E-8
286286
0E-8#!#<NULL>#!#<NULL>#!#<NULL>
287-
<NULL>#!#0E-8#!#<NULL>#!#<NULL>
288-
<NULL>#!#<NULL>#!#0E-8#!#<NULL>
289-
<NULL>#!#<NULL>#!#<NULL>#!#0E-8
290-
128.00000000#!#<NULL>#!#<NULL>#!#<NULL>
287+
0E-8#!#<NULL>#!#<NULL>#!#<NULL>
288+
0E-8#!#<NULL>#!#<NULL>#!#<NULL>
291289
5.00000000#!#100.00000000#!#10000.00000000#!#1000000000.00000000
290+
127.00000000#!#<NULL>#!#<NULL>#!#<NULL>
291+
128.00000000#!#<NULL>#!#<NULL>#!#<NULL>
292292
~~END~~
293293

294294

@@ -580,32 +580,32 @@ SELECT
580580
ROUND(smallint_col + 0.5) AS round_smallint,
581581
ROUND(integer_col + 0.5) AS round_int,
582582
ROUND(bigint_col + 0.5) AS round_bigint
583-
FROM exactnumeric_table;
583+
FROM exactnumeric_table order by round_tinyint;
584584
GO
585585
~~START~~
586586
numeric#!#numeric#!#numeric#!#numeric
587-
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
588-
<NULL>#!#-32768.00000000#!#<NULL>#!#<NULL>
589587
<NULL>#!#<NULL>#!#-2147483648.00000000#!#<NULL>
588+
<NULL>#!#1.00000000#!#<NULL>#!#<NULL>
589+
<NULL>#!#<NULL>#!#1.00000000#!#<NULL>
590590
<NULL>#!#<NULL>#!#<NULL>#!#-9223372036854775808.00000000
591-
128.00000000#!#<NULL>#!#<NULL>#!#<NULL>
591+
<NULL>#!#<NULL>#!#<NULL>#!#1.00000000
592592
<NULL>#!#32768.00000000#!#<NULL>#!#<NULL>
593593
<NULL>#!#<NULL>#!#2147483648.00000000#!#<NULL>
594594
<NULL>#!#<NULL>#!#<NULL>#!#9223372036854775808.00000000
595595
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
596596
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
597597
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
598598
<NULL>#!#<NULL>#!#<NULL>#!#<NULL>
599-
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
599+
<NULL>#!#-32768.00000000#!#<NULL>#!#<NULL>
600600
<NULL>#!#1.00000000#!#<NULL>#!#<NULL>
601601
<NULL>#!#<NULL>#!#1.00000000#!#<NULL>
602602
<NULL>#!#<NULL>#!#<NULL>#!#1.00000000
603603
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
604-
<NULL>#!#1.00000000#!#<NULL>#!#<NULL>
605-
<NULL>#!#<NULL>#!#1.00000000#!#<NULL>
606-
<NULL>#!#<NULL>#!#<NULL>#!#1.00000000
607-
129.00000000#!#<NULL>#!#<NULL>#!#<NULL>
604+
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
605+
1.00000000#!#<NULL>#!#<NULL>#!#<NULL>
608606
6.00000000#!#101.00000000#!#10001.00000000#!#1000000001.00000000
607+
128.00000000#!#<NULL>#!#<NULL>#!#<NULL>
608+
129.00000000#!#<NULL>#!#<NULL>#!#<NULL>
609609
~~END~~
610610

611611

0 commit comments

Comments
 (0)