Skip to content

Commit eccf385

Browse files
tanyagupta17yashneet vinayak
authored and
yashneet vinayak
committed
Handling CASE expression when one branch is of decimal/numeric and other branch is of smallmoney/money. (babelfish-for-postgresql#3719)
Currently, we treat typmod for smallmoney/money datatype as default which leads to output differences in many cases. Following are the fixes introduced in this PR : Introduce typmod handling for smallmoney/money datatype for objects like table/procedure/views/functions/casts and handling their upgrade scenarios. We are also handling p&s for declared variables, nested case expressions and unions having smallmoney/money as one of it's node. It also takes case of round vs truncation behaviour around edge cases for fixeddecimal multiplication Money/smallmoney and Numeric Arithmetic operations - precision and sclae correction and fixing TDS protocol Errors. T_Param node handling for fixeddecimal dataypes (money and smallmoney) when numeric is another operand. T_Aggref handling for Sum/Avg for fixeddecimal and other fixed length datatypes when numeric is another operand. Handing typmod for smallmoney/money in pg catalogs for views metadata and during dump-restore. Handling money/smallmoney typmod and fixeddecimal typmodin during dump-restore and by-passing typmod while deparsing for ISC check constraint. Avoiding crashes and wrong results by updating sanity checks and setting intermittent result size appropirately for bigint and fixeddecimal multiplication. Signed-off-by: Tanya Gupta [email protected] Engine PR : babelfish-for-postgresql/postgresql_modified_for_babelfish#575
1 parent 2593bba commit eccf385

File tree

105 files changed

+6873
-226
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+6873
-226
lines changed

contrib/babelfishpg_money/fixeddecimal.c

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@
3535
*/
3636
#define FIXEDDECIMAL_MULTIPLIER 10000LL
3737

38+
/*
39+
* This ensures that we round up the result in case the 5th decimal place >= 5
40+
* in case of fixeddecimal multiplication.
41+
*/
42+
#define FIXEDDECIMAL_ROUNDUP 5000
43+
3844
/*
3945
* Number of decimal places to store.
4046
* This number should be the number of decimal digits that it takes to
@@ -53,11 +59,9 @@
5359

5460
/*
5561
* This is bounded by the maximum and minimum values of int64.
56-
* 9223372036854775807 is 19 decimal digits long, but we we can only represent
57-
* this number / FIXEDDECIMAL_MULTIPLIER, so we must subtract
58-
* FIXEDDECIMAL_SCALE
62+
* 9223372036854775807 is 19 decimal digits long.
5963
*/
60-
#define FIXEDDECIMAL_MAX_PRECISION 19 - FIXEDDECIMAL_SCALE
64+
#define FIXEDDECIMAL_MAX_PRECISION 19
6165

6266
/* Define this if your compiler has _builtin_add_overflow() */
6367
/* #define HAVE_BUILTIN_OVERFLOW */
@@ -791,14 +795,21 @@ fixeddecimaltypmodin(PG_FUNCTION_ARGS)
791795
}
792796
else if (n == 1)
793797
{
794-
if (tl[0] < FIXEDDECIMAL_SCALE || tl[0] > FIXEDDECIMAL_MAX_PRECISION)
798+
int val_precision = ((tl[0] - VARHDRSZ) >> 16) & 0xffff;
799+
int val_scale = (tl[0] - VARHDRSZ) & 0xffff;
800+
if (val_precision < FIXEDDECIMAL_SCALE || val_precision > FIXEDDECIMAL_MAX_PRECISION)
795801
ereport(ERROR,
796802
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
797803
errmsg("FIXEDDECIMAL precision %d must be between %d and %d",
798-
tl[0], FIXEDDECIMAL_SCALE, FIXEDDECIMAL_MAX_PRECISION)));
804+
val_precision, FIXEDDECIMAL_SCALE, FIXEDDECIMAL_MAX_PRECISION)));
799805

800-
/* scale defaults to FIXEDDECIMAL_SCALE */
801-
typmod = ((tl[0] << 16) | FIXEDDECIMAL_SCALE) + VARHDRSZ;
806+
if (val_scale != FIXEDDECIMAL_SCALE)
807+
ereport(ERROR,
808+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
809+
errmsg("FIXEDDECIMAL scale must be %d",
810+
FIXEDDECIMAL_SCALE)));
811+
812+
typmod = ((val_precision << 16) | val_scale) + VARHDRSZ;
802813
}
803814
else
804815
{
@@ -1731,6 +1742,10 @@ fixeddecimalmul(PG_FUNCTION_ARGS)
17311742
* we must divide the result by this to get the correct result.
17321743
*/
17331744
result = (int128) arg1 * arg2 / FIXEDDECIMAL_MULTIPLIER;
1745+
/* Round off the result to FIXEDDECIMAL_SCALE. */
1746+
if ((((int128) arg1 * arg2 % FIXEDDECIMAL_MULTIPLIER)) >= FIXEDDECIMAL_ROUNDUP)
1747+
result++;
1748+
17341749

17351750
if (result != ((int64) result))
17361751
ereport(ERROR,
@@ -1908,34 +1923,28 @@ fixeddecimalint8mul(PG_FUNCTION_ARGS)
19081923
{
19091924
int64 arg1 = PG_GETARG_INT64(0);
19101925
int64 arg2 = PG_GETARG_INT64(1);
1911-
int64 result;
1926+
int128 result;
19121927

19131928
#ifdef HAVE_BUILTIN_OVERFLOW
19141929
if (__builtin_mul_overflow(arg1, arg2, &result))
19151930
ereport(ERROR,
19161931
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
19171932
errmsg("fixeddecimal out of range")));
19181933
#else
1919-
result = arg1 * arg2;
1934+
result = (int128) arg1 * arg2;
19201935

19211936
/*
1922-
* Overflow check. We basically check to see if result / arg1 gives arg2
1923-
* again. There is one case where this fails: arg1 = 0 (which cannot
1924-
* overflow).
1925-
*
1926-
* Since the division is likely much more expensive than the actual
1927-
* multiplication, we'd like to skip it where possible. The best bang for
1928-
* the buck seems to be to check whether both inputs are in the int32
1929-
* range; if so, no overflow is possible.
1937+
* Overflow check. We should not be testing based only on, if indiviudal arg (agr1)
1938+
* is convertible to int32 as the result could still overflow. Hence we directly check
1939+
* if result is in int64 range; if so, no overflow is possible.
19301940
*/
1931-
if (arg1 != (int64) ((int32) arg1) &&
1932-
result / arg1 != arg2)
1941+
if (result != (int128) ((int64) result))
19331942
ereport(ERROR,
19341943
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
19351944
errmsg("fixeddecimal out of range")));
19361945
#endif /* HAVE_BUILTIN_OVERFLOW */
19371946

1938-
PG_RETURN_INT64(result);
1947+
PG_RETURN_INT64((int64) result);
19391948
}
19401949

19411950
Datum
@@ -2051,34 +2060,28 @@ int8fixeddecimalmul(PG_FUNCTION_ARGS)
20512060
{
20522061
int64 arg1 = PG_GETARG_INT64(0);
20532062
int64 arg2 = PG_GETARG_INT64(1);
2054-
int64 result;
2063+
int128 result;
20552064

20562065
#ifdef HAVE_BUILTIN_OVERFLOW
20572066
if (__builtin_mul_overflow(arg1, arg2, &result))
20582067
ereport(ERROR,
20592068
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
20602069
errmsg("fixeddecimal out of range")));
20612070
#else
2062-
result = arg1 * arg2;
2071+
result = (int128) arg1 * arg2;
20632072

20642073
/*
2065-
* Overflow check. We basically check to see if result / arg2 gives arg1
2066-
* again. There is one case where this fails: arg2 = 0 (which cannot
2067-
* overflow).
2068-
*
2069-
* Since the division is likely much more expensive than the actual
2070-
* multiplication, we'd like to skip it where possible. The best bang for
2071-
* the buck seems to be to check whether both inputs are in the int32
2072-
* range; if so, no overflow is possible.
2074+
* Overflow check. We should not be testing based only on, if indiviudal arg (agr2)
2075+
* is convertible to int32 as the result could still overflow. Hence we directly check
2076+
* if result is in int64 range; if so, no overflow is possible.
20732077
*/
2074-
if (arg2 != (int64) ((int32) arg2) &&
2075-
result / arg2 != arg1)
2078+
if (result != (int128) ((int64) result))
20762079
ereport(ERROR,
20772080
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
20782081
errmsg("fixeddecimal out of range")));
20792082
#endif /* HAVE_BUILTIN_OVERFLOW */
20802083

2081-
PG_RETURN_INT64(result);
2084+
PG_RETURN_INT64((int64) result);
20822085
}
20832086

20842087
Datum

contrib/babelfishpg_tsql/sql/sys_views.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ BEGIN
169169
CASE v_type
170170
WHEN 'decimal' THEN scale = (typemod - 4) & 65535;
171171
WHEN 'numeric' THEN scale = (typemod - 4) & 65535;
172+
WHEN 'money' THEN scale = 4;
173+
WHEN 'smallmoney' THEN scale = 4;
172174
WHEN 'smalldatetime' THEN scale = 0;
173175
WHEN 'datetime2' THEN
174176
CASE typemod
@@ -256,6 +258,8 @@ BEGIN
256258
CASE v_type
257259
WHEN 'numeric' THEN precision = ((typemod - 4) >> 16) & 65535;
258260
WHEN 'decimal' THEN precision = ((typemod - 4) >> 16) & 65535;
261+
WHEN 'money' THEN precision = 19;
262+
WHEN 'smallmoney' THEN precision = 10;
259263
WHEN 'smalldatetime' THEN precision = 16;
260264
WHEN 'datetime2' THEN
261265
CASE typemod

0 commit comments

Comments
 (0)