Skip to content

Commit 22b0ccd

Browse files
Add overflow checks to money type.
None of the arithmetic functions for the the money type handle overflow. This commit introduces several helper functions with overflow checking and makes use of them in the money type's arithmetic functions. Fixes bug #18240. Reported-by: Alexander Lakhin Author: Joseph Koshakow Discussion: https://postgr.es/m/18240-c5da758d7dc1ecf0%40postgresql.org Discussion: https://postgr.es/m/CAAvxfHdBPOyEGS7s%2Bxf4iaW0-cgiq25jpYdWBqQqvLtLe_t6tw%40mail.gmail.com Backpatch-through: 12
1 parent aa60798 commit 22b0ccd

File tree

3 files changed

+124
-80
lines changed

3 files changed

+124
-80
lines changed

src/backend/utils/adt/cash.c

Lines changed: 94 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "libpq/pqformat.h"
2727
#include "utils/builtins.h"
2828
#include "utils/cash.h"
29+
#include "utils/float.h"
2930
#include "utils/numeric.h"
3031
#include "utils/pg_locale.h"
3132

@@ -86,6 +87,82 @@ num_word(Cash value)
8687
return buf;
8788
} /* num_word() */
8889

90+
static inline Cash
91+
cash_pl_cash(Cash c1, Cash c2)
92+
{
93+
Cash res;
94+
95+
if (unlikely(pg_add_s64_overflow(c1, c2, &res)))
96+
ereport(ERROR,
97+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
98+
errmsg("money out of range")));
99+
100+
return res;
101+
}
102+
103+
static inline Cash
104+
cash_mi_cash(Cash c1, Cash c2)
105+
{
106+
Cash res;
107+
108+
if (unlikely(pg_sub_s64_overflow(c1, c2, &res)))
109+
ereport(ERROR,
110+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
111+
errmsg("money out of range")));
112+
113+
return res;
114+
}
115+
116+
static inline Cash
117+
cash_mul_float8(Cash c, float8 f)
118+
{
119+
float8 res = rint(float8_mul((float8) c, f));
120+
121+
if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
122+
ereport(ERROR,
123+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
124+
errmsg("money out of range")));
125+
126+
return (Cash) res;
127+
}
128+
129+
static inline Cash
130+
cash_div_float8(Cash c, float8 f)
131+
{
132+
float8 res = rint(float8_div((float8) c, f));
133+
134+
if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
135+
ereport(ERROR,
136+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
137+
errmsg("money out of range")));
138+
139+
return (Cash) res;
140+
}
141+
142+
static inline Cash
143+
cash_mul_int64(Cash c, int64 i)
144+
{
145+
Cash res;
146+
147+
if (unlikely(pg_mul_s64_overflow(c, i, &res)))
148+
ereport(ERROR,
149+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
150+
errmsg("money out of range")));
151+
152+
return res;
153+
}
154+
155+
static inline Cash
156+
cash_div_int64(Cash c, int64 i)
157+
{
158+
if (unlikely(i == 0))
159+
ereport(ERROR,
160+
(errcode(ERRCODE_DIVISION_BY_ZERO),
161+
errmsg("division by zero")));
162+
163+
return c / i;
164+
}
165+
89166
/* cash_in()
90167
* Convert a string to a cash data type.
91168
* Format is [$]###[,]###[.##]
@@ -612,11 +689,8 @@ cash_pl(PG_FUNCTION_ARGS)
612689
{
613690
Cash c1 = PG_GETARG_CASH(0);
614691
Cash c2 = PG_GETARG_CASH(1);
615-
Cash result;
616-
617-
result = c1 + c2;
618692

619-
PG_RETURN_CASH(result);
693+
PG_RETURN_CASH(cash_pl_cash(c1, c2));
620694
}
621695

622696

@@ -628,11 +702,8 @@ cash_mi(PG_FUNCTION_ARGS)
628702
{
629703
Cash c1 = PG_GETARG_CASH(0);
630704
Cash c2 = PG_GETARG_CASH(1);
631-
Cash result;
632-
633-
result = c1 - c2;
634705

635-
PG_RETURN_CASH(result);
706+
PG_RETURN_CASH(cash_mi_cash(c1, c2));
636707
}
637708

638709

@@ -664,10 +735,8 @@ cash_mul_flt8(PG_FUNCTION_ARGS)
664735
{
665736
Cash c = PG_GETARG_CASH(0);
666737
float8 f = PG_GETARG_FLOAT8(1);
667-
Cash result;
668738

669-
result = rint(c * f);
670-
PG_RETURN_CASH(result);
739+
PG_RETURN_CASH(cash_mul_float8(c, f));
671740
}
672741

673742

@@ -679,10 +748,8 @@ flt8_mul_cash(PG_FUNCTION_ARGS)
679748
{
680749
float8 f = PG_GETARG_FLOAT8(0);
681750
Cash c = PG_GETARG_CASH(1);
682-
Cash result;
683751

684-
result = rint(f * c);
685-
PG_RETURN_CASH(result);
752+
PG_RETURN_CASH(cash_mul_float8(c, f));
686753
}
687754

688755

@@ -694,15 +761,8 @@ cash_div_flt8(PG_FUNCTION_ARGS)
694761
{
695762
Cash c = PG_GETARG_CASH(0);
696763
float8 f = PG_GETARG_FLOAT8(1);
697-
Cash result;
698764

699-
if (f == 0.0)
700-
ereport(ERROR,
701-
(errcode(ERRCODE_DIVISION_BY_ZERO),
702-
errmsg("division by zero")));
703-
704-
result = rint(c / f);
705-
PG_RETURN_CASH(result);
765+
PG_RETURN_CASH(cash_div_float8(c, f));
706766
}
707767

708768

@@ -714,10 +774,8 @@ cash_mul_flt4(PG_FUNCTION_ARGS)
714774
{
715775
Cash c = PG_GETARG_CASH(0);
716776
float4 f = PG_GETARG_FLOAT4(1);
717-
Cash result;
718777

719-
result = rint(c * (float8) f);
720-
PG_RETURN_CASH(result);
778+
PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
721779
}
722780

723781

@@ -729,10 +787,8 @@ flt4_mul_cash(PG_FUNCTION_ARGS)
729787
{
730788
float4 f = PG_GETARG_FLOAT4(0);
731789
Cash c = PG_GETARG_CASH(1);
732-
Cash result;
733790

734-
result = rint((float8) f * c);
735-
PG_RETURN_CASH(result);
791+
PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
736792
}
737793

738794

@@ -745,15 +801,8 @@ cash_div_flt4(PG_FUNCTION_ARGS)
745801
{
746802
Cash c = PG_GETARG_CASH(0);
747803
float4 f = PG_GETARG_FLOAT4(1);
748-
Cash result;
749-
750-
if (f == 0.0)
751-
ereport(ERROR,
752-
(errcode(ERRCODE_DIVISION_BY_ZERO),
753-
errmsg("division by zero")));
754804

755-
result = rint(c / (float8) f);
756-
PG_RETURN_CASH(result);
805+
PG_RETURN_CASH(cash_div_float8(c, (float8) f));
757806
}
758807

759808

@@ -765,10 +814,8 @@ cash_mul_int8(PG_FUNCTION_ARGS)
765814
{
766815
Cash c = PG_GETARG_CASH(0);
767816
int64 i = PG_GETARG_INT64(1);
768-
Cash result;
769817

770-
result = c * i;
771-
PG_RETURN_CASH(result);
818+
PG_RETURN_CASH(cash_mul_int64(c, i));
772819
}
773820

774821

@@ -780,10 +827,8 @@ int8_mul_cash(PG_FUNCTION_ARGS)
780827
{
781828
int64 i = PG_GETARG_INT64(0);
782829
Cash c = PG_GETARG_CASH(1);
783-
Cash result;
784830

785-
result = i * c;
786-
PG_RETURN_CASH(result);
831+
PG_RETURN_CASH(cash_mul_int64(c, i));
787832
}
788833

789834
/* cash_div_int8()
@@ -794,16 +839,8 @@ cash_div_int8(PG_FUNCTION_ARGS)
794839
{
795840
Cash c = PG_GETARG_CASH(0);
796841
int64 i = PG_GETARG_INT64(1);
797-
Cash result;
798-
799-
if (i == 0)
800-
ereport(ERROR,
801-
(errcode(ERRCODE_DIVISION_BY_ZERO),
802-
errmsg("division by zero")));
803842

804-
result = c / i;
805-
806-
PG_RETURN_CASH(result);
843+
PG_RETURN_CASH(cash_div_int64(c, i));
807844
}
808845

809846

@@ -815,10 +852,8 @@ cash_mul_int4(PG_FUNCTION_ARGS)
815852
{
816853
Cash c = PG_GETARG_CASH(0);
817854
int32 i = PG_GETARG_INT32(1);
818-
Cash result;
819855

820-
result = c * i;
821-
PG_RETURN_CASH(result);
856+
PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
822857
}
823858

824859

@@ -830,10 +865,8 @@ int4_mul_cash(PG_FUNCTION_ARGS)
830865
{
831866
int32 i = PG_GETARG_INT32(0);
832867
Cash c = PG_GETARG_CASH(1);
833-
Cash result;
834868

835-
result = i * c;
836-
PG_RETURN_CASH(result);
869+
PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
837870
}
838871

839872

@@ -846,16 +879,8 @@ cash_div_int4(PG_FUNCTION_ARGS)
846879
{
847880
Cash c = PG_GETARG_CASH(0);
848881
int32 i = PG_GETARG_INT32(1);
849-
Cash result;
850-
851-
if (i == 0)
852-
ereport(ERROR,
853-
(errcode(ERRCODE_DIVISION_BY_ZERO),
854-
errmsg("division by zero")));
855-
856-
result = c / i;
857882

858-
PG_RETURN_CASH(result);
883+
PG_RETURN_CASH(cash_div_int64(c, (int64) i));
859884
}
860885

861886

@@ -867,10 +892,8 @@ cash_mul_int2(PG_FUNCTION_ARGS)
867892
{
868893
Cash c = PG_GETARG_CASH(0);
869894
int16 s = PG_GETARG_INT16(1);
870-
Cash result;
871895

872-
result = c * s;
873-
PG_RETURN_CASH(result);
896+
PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
874897
}
875898

876899
/* int2_mul_cash()
@@ -881,10 +904,8 @@ int2_mul_cash(PG_FUNCTION_ARGS)
881904
{
882905
int16 s = PG_GETARG_INT16(0);
883906
Cash c = PG_GETARG_CASH(1);
884-
Cash result;
885907

886-
result = s * c;
887-
PG_RETURN_CASH(result);
908+
PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
888909
}
889910

890911
/* cash_div_int2()
@@ -896,15 +917,8 @@ cash_div_int2(PG_FUNCTION_ARGS)
896917
{
897918
Cash c = PG_GETARG_CASH(0);
898919
int16 s = PG_GETARG_INT16(1);
899-
Cash result;
900920

901-
if (s == 0)
902-
ereport(ERROR,
903-
(errcode(ERRCODE_DIVISION_BY_ZERO),
904-
errmsg("division by zero")));
905-
906-
result = c / s;
907-
PG_RETURN_CASH(result);
921+
PG_RETURN_CASH(cash_div_int64(c, (int64) s));
908922
}
909923

910924
/* cashlarger()

src/test/regress/expected/money.out

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,3 +528,22 @@ SELECT '-92233720368547758.08'::money::numeric;
528528
-92233720368547758.08
529529
(1 row)
530530

531+
-- overflow checks
532+
SELECT '92233720368547758.07'::money + '0.01'::money;
533+
ERROR: money out of range
534+
SELECT '-92233720368547758.08'::money - '0.01'::money;
535+
ERROR: money out of range
536+
SELECT '92233720368547758.07'::money * 2::float8;
537+
ERROR: money out of range
538+
SELECT '-1'::money / 1.175494e-38::float4;
539+
ERROR: money out of range
540+
SELECT '92233720368547758.07'::money * 2::int4;
541+
ERROR: money out of range
542+
SELECT '1'::money / 0::int2;
543+
ERROR: division by zero
544+
SELECT '42'::money * 'inf'::float8;
545+
ERROR: money out of range
546+
SELECT '42'::money * '-inf'::float8;
547+
ERROR: money out of range
548+
SELECT '42'::money * 'nan'::float4;
549+
ERROR: money out of range

src/test/regress/sql/money.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,14 @@ SELECT '12345678901234567'::money::numeric;
135135
SELECT '-12345678901234567'::money::numeric;
136136
SELECT '92233720368547758.07'::money::numeric;
137137
SELECT '-92233720368547758.08'::money::numeric;
138+
139+
-- overflow checks
140+
SELECT '92233720368547758.07'::money + '0.01'::money;
141+
SELECT '-92233720368547758.08'::money - '0.01'::money;
142+
SELECT '92233720368547758.07'::money * 2::float8;
143+
SELECT '-1'::money / 1.175494e-38::float4;
144+
SELECT '92233720368547758.07'::money * 2::int4;
145+
SELECT '1'::money / 0::int2;
146+
SELECT '42'::money * 'inf'::float8;
147+
SELECT '42'::money * '-inf'::float8;
148+
SELECT '42'::money * 'nan'::float4;

0 commit comments

Comments
 (0)