From 865b5cafa4e8326d73256071c898fe372852e284 Mon Sep 17 00:00:00 2001 From: Rohit Bhagat Date: Fri, 7 Feb 2025 10:17:08 +0000 Subject: [PATCH] fixed issue with computation of precision and scale for non-exact numeric types Signed-off-by: Rohit Bhagat --- .../src/backend/tds/tdsresponse.c | 51 ++++++++++++++++++- contrib/babelfishpg_tsql/src/hooks.c | 21 +++++++- test/JDBC/expected/BABEL-1000.out | 4 +- test/JDBC/expected/BABEL-1164.out | 2 +- test/JDBC/expected/BABEL-3066-vu-verify.out | 19 +++---- test/JDBC/expected/BABEL-381.out | 4 +- test/JDBC/expected/BABEL-5467-vu-verify.out | 8 +-- .../expected/BABEL-CASE_EXPR-vu-verify.out | 2 +- test/JDBC/expected/NUMERIC_TYPMOD.out | 8 +-- test/JDBC/expected/TestDecimal-vu-verify.out | 2 +- test/JDBC/expected/TestDecimal.out | 2 +- test/JDBC/expected/TestNumeric-vu-verify.out | 2 +- test/JDBC/expected/TestNumeric.out | 2 +- test/JDBC/expected/babel_datatype.out | 2 +- .../expected/money_aggregate-vu-verify.out | 2 +- test/JDBC/input/BABEL-5467-vu-verify.sql | 8 +-- 16 files changed, 103 insertions(+), 36 deletions(-) diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c b/contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c index ed2025810d..4573198643 100644 --- a/contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c @@ -140,6 +140,9 @@ static Oid tsql_int4_bit_oid = InvalidOid; static Oid sys_nspoid = InvalidOid; static Oid tsql_bit_oid = InvalidOid; static Oid tsql_fixeddecimal_oid = InvalidOid; +static Oid tsql_tinyint_oid = InvalidOid; +static Oid tsql_money_oid = InvalidOid; +static Oid tsql_smallmoney_oid = InvalidOid; static void FillTabNameWithNumParts(StringInfo buf, uint8 numParts, TdsRelationMetaDataInfo relMetaDataInfo); static void FillTabNameWithoutNumParts(StringInfo buf, uint8 numParts, TdsRelationMetaDataInfo relMetaDataInfo); @@ -638,11 +641,57 @@ is_numeric_datatype(Oid typid) return decimal_oid == typid; } +/* + * is_exact_numeric_datatype - returns bool if given datatype is numeric, decimal, int, tinyint, smallint, bigint, bit, money and smallmoney. + */ +static bool +is_exact_numeric_datatype(Oid typid) +{ + typid = getBaseType(typid); + + if (typid == NUMERICOID || typid == INT2OID || typid == INT4OID || typid == INT8OID) + { + return true; + } + if (!OidIsValid(decimal_oid)) + { + TypeName *typename = makeTypeNameFromNameList(list_make2(makeString("sys"), makeString("decimal"))); + decimal_oid = LookupTypeNameOid(NULL, typename, false); + } + if (!OidIsValid(tsql_tinyint_oid)) + { + TypeName *typename = makeTypeNameFromNameList(list_make2(makeString("sys"), makeString("tinyint"))); + tsql_tinyint_oid = LookupTypeNameOid(NULL, typename, false); + } + if (!OidIsValid(tsql_money_oid)) + { + TypeName *typename = makeTypeNameFromNameList(list_make2(makeString("sys"), makeString("money"))); + tsql_money_oid = LookupTypeNameOid(NULL, typename, false); + } + if (!OidIsValid(tsql_smallmoney_oid)) + { + TypeName *typename = makeTypeNameFromNameList(list_make2(makeString("sys"), makeString("smallmoney"))); + tsql_smallmoney_oid = LookupTypeNameOid(NULL, typename, false); + } + if (!OidIsValid(tsql_bit_oid)) + { + TypeName *typename = makeTypeNameFromNameList(list_make2(makeString("sys"), makeString("bit"))); + tsql_bit_oid = LookupTypeNameOid(NULL, typename, false); + } + + return (decimal_oid == typid + || tsql_tinyint_oid == typid + || tsql_money_oid == typid + || tsql_smallmoney_oid == typid + || tsql_bit_oid == typid); +} + + /* look for a typmod to return from a numeric expression */ int32 resolve_numeric_typmod_from_exp(Plan *plan, Node *expr) { - if (expr == NULL) + if (expr == NULL || !is_exact_numeric_datatype(exprType(expr))) return -1; switch (nodeTag(expr)) { diff --git a/contrib/babelfishpg_tsql/src/hooks.c b/contrib/babelfishpg_tsql/src/hooks.c index b790be564c..c7f964bce8 100644 --- a/contrib/babelfishpg_tsql/src/hooks.c +++ b/contrib/babelfishpg_tsql/src/hooks.c @@ -6075,7 +6075,24 @@ remove_db_name_in_schema(const char *object_name, const char *object_type) static int32 tsql_get_numeric_typmod_from_exp(Plan *plan, Node *expr) { + int32 result_typmod = -1; + uint8_t scale, + precision; if (pltsql_protocol_plugin_ptr && *pltsql_protocol_plugin_ptr && (*pltsql_protocol_plugin_ptr)->get_numeric_typmod_from_exp) - return (*pltsql_protocol_plugin_ptr)->get_numeric_typmod_from_exp(plan, expr); - return -1; + result_typmod = (*pltsql_protocol_plugin_ptr)->get_numeric_typmod_from_exp(plan, expr); + if (result_typmod != -1) + { + /* + * If we are unable to get correct precision and scale for overflow cases + * then return -1 + */ + scale = (result_typmod - VARHDRSZ) & 0xffff; + precision = ((result_typmod - VARHDRSZ) >> 16) & 0xffff; + if (precision > TDS_NUMERIC_MAX_PRECISION) + { + if (!(precision - scale > 32 && scale > 6) && !(precision - scale <= TDS_NUMERIC_MAX_PRECISION)) + return -1; + } + } + return result_typmod; } diff --git a/test/JDBC/expected/BABEL-1000.out b/test/JDBC/expected/BABEL-1000.out index 4d2ca730e0..30fb179dc7 100644 --- a/test/JDBC/expected/BABEL-1000.out +++ b/test/JDBC/expected/BABEL-1000.out @@ -162,14 +162,14 @@ SELECT * FROM babel_1000_test8(1) GO ~~START~~ numeric -2.06000000 +2.06 ~~END~~ SELECT * FROM babel_1000_test8(12.345678) GO ~~START~~ numeric -13.06000000 +13.06 ~~END~~ -- overflow, expect error diff --git a/test/JDBC/expected/BABEL-1164.out b/test/JDBC/expected/BABEL-1164.out index 4d6a7a5de1..48be6277cd 100644 --- a/test/JDBC/expected/BABEL-1164.out +++ b/test/JDBC/expected/BABEL-1164.out @@ -43,7 +43,7 @@ SELECT * FROM t2; GO ~~START~~ int#!#int#!#numeric#!#numeric -1#!#1#!#2#!#2.00000000 +1#!#1#!#2#!#2 ~~END~~ diff --git a/test/JDBC/expected/BABEL-3066-vu-verify.out b/test/JDBC/expected/BABEL-3066-vu-verify.out index 4286cc7fe9..2bff580f23 100644 --- a/test/JDBC/expected/BABEL-3066-vu-verify.out +++ b/test/JDBC/expected/BABEL-3066-vu-verify.out @@ -504,7 +504,7 @@ SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) - CAST(CAST(12465781 GO ~~START~~ numeric -0 +0E-15 ~~END~~ @@ -512,7 +512,7 @@ SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) - CAST(CAST(12465781 GO ~~START~~ numeric -0 +0E-10 ~~END~~ @@ -540,9 +540,10 @@ GO SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) * CAST(CAST(12465781.4679213254 as real) as numeric(38,15)); GO -~~ERROR (Code: 33557097)~~ - -~~ERROR (Message: Arithmetic overflow error for data type numeric.)~~ +~~START~~ +numeric +155395695939961.000000 +~~END~~ SELECT CAST(CAST(CAST(CAST(12465781.46792 as real) as numeric(38,10)) * CAST(CAST(12465781.4679213254 as real) as numeric(38,15)) as real) as numeric(38,6)); @@ -557,7 +558,7 @@ SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) * CAST(CAST(12465781 GO ~~START~~ numeric -155395695939961.00000000000000000000 +155395695939961.000000 ~~END~~ @@ -565,7 +566,7 @@ SELECT CAST(CAST(12465781.46792 as real) as numeric(38,0)) / CAST(CAST(12465781. GO ~~START~~ numeric -1.00000000000000000000 +1.000000 ~~END~~ @@ -587,7 +588,7 @@ SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) / CAST(CAST(12465781 GO ~~START~~ numeric -1.00000000000000000000 +1.000000 ~~END~~ @@ -595,6 +596,6 @@ SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) / CAST(CAST(12465781 GO ~~START~~ numeric -1.00000000000000000000 +1.000000 ~~END~~ diff --git a/test/JDBC/expected/BABEL-381.out b/test/JDBC/expected/BABEL-381.out index ec8e4a0dbd..63855908b8 100644 --- a/test/JDBC/expected/BABEL-381.out +++ b/test/JDBC/expected/BABEL-381.out @@ -12,7 +12,7 @@ select 2.0/1.5; go ~~START~~ numeric -1.3333333333333333 +1.333333 ~~END~~ @@ -20,6 +20,6 @@ select 2.0, 2.0/1.5, 1.0/1.5; go ~~START~~ numeric#!#numeric#!#numeric -2.0#!#1.3333333333333333#!#0.66666666666666666667 +2.0#!#1.333333#!#0.666666 ~~END~~ diff --git a/test/JDBC/expected/BABEL-5467-vu-verify.out b/test/JDBC/expected/BABEL-5467-vu-verify.out index d4f01c66b8..d415a7ab04 100644 --- a/test/JDBC/expected/BABEL-5467-vu-verify.out +++ b/test/JDBC/expected/BABEL-5467-vu-verify.out @@ -134,7 +134,7 @@ go -- Precision and scale details should be stored correctly select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE from information_schema.columns -where TABLE_NAME = 'babel_5467_avgdata_1' +where TABLE_NAME = 'babel_5467_avgdata_1' order by COLUMN_NAME go ~~START~~ nvarchar#!#nvarchar#!#nvarchar#!#nvarchar#!#tinyint#!#smallint#!#int @@ -145,7 +145,7 @@ master#!#dbo#!#babel_5467_avgdata_1#!#count_val#!#38#!#10#!#6 select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE from information_schema.columns -where TABLE_NAME = 'babel_5467_avgdata_2' +where TABLE_NAME = 'babel_5467_avgdata_2' order by COLUMN_NAME go ~~START~~ nvarchar#!#nvarchar#!#nvarchar#!#nvarchar#!#tinyint#!#smallint#!#int @@ -156,7 +156,7 @@ master#!#dbo#!#babel_5467_avgdata_2#!#count_val#!#38#!#10#!#6 select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE from information_schema.columns -where TABLE_NAME = 'babel_5467_avgdata_3' +where TABLE_NAME = 'babel_5467_avgdata_3' order by COLUMN_NAME go ~~START~~ nvarchar#!#nvarchar#!#nvarchar#!#nvarchar#!#tinyint#!#smallint#!#int @@ -167,7 +167,7 @@ master#!#dbo#!#babel_5467_avgdata_3#!#count_val#!#38#!#10#!#6 select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE from information_schema.columns -where TABLE_NAME = 'babel_5467_t1' +where TABLE_NAME = 'babel_5467_t1' order by COLUMN_NAME go ~~START~~ nvarchar#!#nvarchar#!#nvarchar#!#nvarchar#!#tinyint#!#smallint#!#int diff --git a/test/JDBC/expected/BABEL-CASE_EXPR-vu-verify.out b/test/JDBC/expected/BABEL-CASE_EXPR-vu-verify.out index 9a83636803..b80f9636f6 100644 --- a/test/JDBC/expected/BABEL-CASE_EXPR-vu-verify.out +++ b/test/JDBC/expected/BABEL-CASE_EXPR-vu-verify.out @@ -13956,7 +13956,7 @@ END AS RESULT GO ~~START~~ numeric -1234568.89010000 +1234568.8901000000 ~~END~~ diff --git a/test/JDBC/expected/NUMERIC_TYPMOD.out b/test/JDBC/expected/NUMERIC_TYPMOD.out index a4f12d6360..02f7a20671 100644 --- a/test/JDBC/expected/NUMERIC_TYPMOD.out +++ b/test/JDBC/expected/NUMERIC_TYPMOD.out @@ -1666,7 +1666,7 @@ END AS result; GO ~~START~~ numeric -6790.080000 +6790.080000000000000 ~~END~~ @@ -1682,7 +1682,7 @@ END AS result; GO ~~START~~ numeric -6790.080000 +6790.080000000000000 ~~END~~ @@ -1698,7 +1698,7 @@ END AS result; GO ~~START~~ numeric -6790.080000 +6790.080000000000000 ~~END~~ @@ -1714,7 +1714,7 @@ END AS result; GO ~~START~~ numeric -6790.080000 +6790.080000000000000 ~~END~~ diff --git a/test/JDBC/expected/TestDecimal-vu-verify.out b/test/JDBC/expected/TestDecimal-vu-verify.out index 81b25178c1..8c8c89d7b5 100644 --- a/test/JDBC/expected/TestDecimal-vu-verify.out +++ b/test/JDBC/expected/TestDecimal-vu-verify.out @@ -239,7 +239,7 @@ numeric#!#numeric#!#numeric#!#numeric#!#numeric#!#numeric select COALESCE( int1, int2 / COALESCE( NULLIF( rate, 0 ), 1 )) * CASE WHEN type IS NULL THEN 1 ELSE -1 END AS FTD_AMOUNT_FC from testdecimal_vu_prepare_tab1; ~~START~~ numeric -1879323.222200 +1879323.222200000000 ~~END~~ diff --git a/test/JDBC/expected/TestDecimal.out b/test/JDBC/expected/TestDecimal.out index 9db9408d9f..a99586711d 100644 --- a/test/JDBC/expected/TestDecimal.out +++ b/test/JDBC/expected/TestDecimal.out @@ -798,7 +798,7 @@ insert into testdecimal_tab values (1879323.2222, 2383823.343, 4.5484, NULL); select COALESCE( int1, int2 / COALESCE( NULLIF( rate, 0 ), 1 )) * CASE WHEN type IS NULL THEN 1 ELSE -1 END AS FTD_AMOUNT_FC from testdecimal_tab; ~~START~~ numeric -1879323.222200 +1879323.222200000000 ~~END~~ drop table testdecimal_tab; diff --git a/test/JDBC/expected/TestNumeric-vu-verify.out b/test/JDBC/expected/TestNumeric-vu-verify.out index e7a43199c1..50ad723ceb 100644 --- a/test/JDBC/expected/TestNumeric-vu-verify.out +++ b/test/JDBC/expected/TestNumeric-vu-verify.out @@ -436,7 +436,7 @@ numeric#!#numeric#!#numeric#!#numeric#!#numeric#!#numeric select COALESCE( int1, int2 / COALESCE( NULLIF( rate, 0 ), 1 )) * CASE WHEN type IS NULL THEN 1 ELSE -1 END AS FTD_AMOUNT_FC from testnumeric_vu_prepare_tab1; ~~START~~ numeric -1879323.222200 +1879323.222200000000 ~~END~~ diff --git a/test/JDBC/expected/TestNumeric.out b/test/JDBC/expected/TestNumeric.out index acc5b5f428..f2d6e3862d 100644 --- a/test/JDBC/expected/TestNumeric.out +++ b/test/JDBC/expected/TestNumeric.out @@ -985,7 +985,7 @@ insert into testnumeric_tab values (1879323.2222, 2383823.343, 4.5484, NULL); select COALESCE( int1, int2 / COALESCE( NULLIF( rate, 0 ), 1 )) * CASE WHEN type IS NULL THEN 1 ELSE -1 END AS FTD_AMOUNT_FC from testnumeric_tab; ~~START~~ numeric -1879323.222200 +1879323.222200000000 ~~END~~ drop table testnumeric_tab; diff --git a/test/JDBC/expected/babel_datatype.out b/test/JDBC/expected/babel_datatype.out index 3ba0c04eed..463657052d 100644 --- a/test/JDBC/expected/babel_datatype.out +++ b/test/JDBC/expected/babel_datatype.out @@ -607,7 +607,7 @@ select CAST(2 AS money) / CAST(0.5 AS numeric(4,2)); GO ~~START~~ numeric -4.000000 +4.0000000 ~~END~~ select CAST(2 AS money) / CAST(0.5 AS decimal(4,2)); diff --git a/test/JDBC/expected/money_aggregate-vu-verify.out b/test/JDBC/expected/money_aggregate-vu-verify.out index 866fbead8c..eab04bae1d 100644 --- a/test/JDBC/expected/money_aggregate-vu-verify.out +++ b/test/JDBC/expected/money_aggregate-vu-verify.out @@ -209,6 +209,6 @@ varchar#!#varchar#!#smallint#!#tinyint#!#tinyint avgamount#!#money#!#8#!#19#!#4 maxamount#!#money#!#8#!#19#!#4 minamount#!#money#!#8#!#19#!#4 -totalwithmarkup#!#numeric#!#17#!#38#!#38 +totalwithmarkup#!#numeric#!#5#!#5#!#2 ~~END~~ diff --git a/test/JDBC/input/BABEL-5467-vu-verify.sql b/test/JDBC/input/BABEL-5467-vu-verify.sql index 7d7f68291e..0b86492374 100644 --- a/test/JDBC/input/BABEL-5467-vu-verify.sql +++ b/test/JDBC/input/BABEL-5467-vu-verify.sql @@ -77,22 +77,22 @@ go -- Precision and scale details should be stored correctly select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE from information_schema.columns -where TABLE_NAME = 'babel_5467_avgdata_1' +where TABLE_NAME = 'babel_5467_avgdata_1' order by COLUMN_NAME go select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE from information_schema.columns -where TABLE_NAME = 'babel_5467_avgdata_2' +where TABLE_NAME = 'babel_5467_avgdata_2' order by COLUMN_NAME go select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE from information_schema.columns -where TABLE_NAME = 'babel_5467_avgdata_3' +where TABLE_NAME = 'babel_5467_avgdata_3' order by COLUMN_NAME go select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE from information_schema.columns -where TABLE_NAME = 'babel_5467_t1' +where TABLE_NAME = 'babel_5467_t1' order by COLUMN_NAME go -- tables with computed columns having expression which results in numeric