|
35 | 35 | */
|
36 | 36 | #define FIXEDDECIMAL_MULTIPLIER 10000LL
|
37 | 37 |
|
| 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 | + |
38 | 44 | /*
|
39 | 45 | * Number of decimal places to store.
|
40 | 46 | * This number should be the number of decimal digits that it takes to
|
|
53 | 59 |
|
54 | 60 | /*
|
55 | 61 | * 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. |
59 | 63 | */
|
60 |
| -#define FIXEDDECIMAL_MAX_PRECISION 19 - FIXEDDECIMAL_SCALE |
| 64 | +#define FIXEDDECIMAL_MAX_PRECISION 19 |
61 | 65 |
|
62 | 66 | /* Define this if your compiler has _builtin_add_overflow() */
|
63 | 67 | /* #define HAVE_BUILTIN_OVERFLOW */
|
@@ -791,14 +795,21 @@ fixeddecimaltypmodin(PG_FUNCTION_ARGS)
|
791 | 795 | }
|
792 | 796 | else if (n == 1)
|
793 | 797 | {
|
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) |
795 | 801 | ereport(ERROR,
|
796 | 802 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
797 | 803 | 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))); |
799 | 805 |
|
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; |
802 | 813 | }
|
803 | 814 | else
|
804 | 815 | {
|
@@ -1731,6 +1742,10 @@ fixeddecimalmul(PG_FUNCTION_ARGS)
|
1731 | 1742 | * we must divide the result by this to get the correct result.
|
1732 | 1743 | */
|
1733 | 1744 | 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 | + |
1734 | 1749 |
|
1735 | 1750 | if (result != ((int64) result))
|
1736 | 1751 | ereport(ERROR,
|
@@ -1908,34 +1923,28 @@ fixeddecimalint8mul(PG_FUNCTION_ARGS)
|
1908 | 1923 | {
|
1909 | 1924 | int64 arg1 = PG_GETARG_INT64(0);
|
1910 | 1925 | int64 arg2 = PG_GETARG_INT64(1);
|
1911 |
| - int64 result; |
| 1926 | + int128 result; |
1912 | 1927 |
|
1913 | 1928 | #ifdef HAVE_BUILTIN_OVERFLOW
|
1914 | 1929 | if (__builtin_mul_overflow(arg1, arg2, &result))
|
1915 | 1930 | ereport(ERROR,
|
1916 | 1931 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
1917 | 1932 | errmsg("fixeddecimal out of range")));
|
1918 | 1933 | #else
|
1919 |
| - result = arg1 * arg2; |
| 1934 | + result = (int128) arg1 * arg2; |
1920 | 1935 |
|
1921 | 1936 | /*
|
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. |
1930 | 1940 | */
|
1931 |
| - if (arg1 != (int64) ((int32) arg1) && |
1932 |
| - result / arg1 != arg2) |
| 1941 | + if (result != (int128) ((int64) result)) |
1933 | 1942 | ereport(ERROR,
|
1934 | 1943 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
1935 | 1944 | errmsg("fixeddecimal out of range")));
|
1936 | 1945 | #endif /* HAVE_BUILTIN_OVERFLOW */
|
1937 | 1946 |
|
1938 |
| - PG_RETURN_INT64(result); |
| 1947 | + PG_RETURN_INT64((int64) result); |
1939 | 1948 | }
|
1940 | 1949 |
|
1941 | 1950 | Datum
|
@@ -2051,34 +2060,28 @@ int8fixeddecimalmul(PG_FUNCTION_ARGS)
|
2051 | 2060 | {
|
2052 | 2061 | int64 arg1 = PG_GETARG_INT64(0);
|
2053 | 2062 | int64 arg2 = PG_GETARG_INT64(1);
|
2054 |
| - int64 result; |
| 2063 | + int128 result; |
2055 | 2064 |
|
2056 | 2065 | #ifdef HAVE_BUILTIN_OVERFLOW
|
2057 | 2066 | if (__builtin_mul_overflow(arg1, arg2, &result))
|
2058 | 2067 | ereport(ERROR,
|
2059 | 2068 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
2060 | 2069 | errmsg("fixeddecimal out of range")));
|
2061 | 2070 | #else
|
2062 |
| - result = arg1 * arg2; |
| 2071 | + result = (int128) arg1 * arg2; |
2063 | 2072 |
|
2064 | 2073 | /*
|
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. |
2073 | 2077 | */
|
2074 |
| - if (arg2 != (int64) ((int32) arg2) && |
2075 |
| - result / arg2 != arg1) |
| 2078 | + if (result != (int128) ((int64) result)) |
2076 | 2079 | ereport(ERROR,
|
2077 | 2080 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
2078 | 2081 | errmsg("fixeddecimal out of range")));
|
2079 | 2082 | #endif /* HAVE_BUILTIN_OVERFLOW */
|
2080 | 2083 |
|
2081 |
| - PG_RETURN_INT64(result); |
| 2084 | + PG_RETURN_INT64((int64) result); |
2082 | 2085 | }
|
2083 | 2086 |
|
2084 | 2087 | Datum
|
|
0 commit comments