Skip to content

Commit e8dfe04

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 d42f60c commit e8dfe04

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/int8.h"
3031
#include "utils/numeric.h"
3132
#include "utils/pg_locale.h"
@@ -87,6 +88,82 @@ num_word(Cash value)
8788
return buf;
8889
} /* num_word() */
8990

91+
static inline Cash
92+
cash_pl_cash(Cash c1, Cash c2)
93+
{
94+
Cash res;
95+
96+
if (unlikely(pg_add_s64_overflow(c1, c2, &res)))
97+
ereport(ERROR,
98+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
99+
errmsg("money out of range")));
100+
101+
return res;
102+
}
103+
104+
static inline Cash
105+
cash_mi_cash(Cash c1, Cash c2)
106+
{
107+
Cash res;
108+
109+
if (unlikely(pg_sub_s64_overflow(c1, c2, &res)))
110+
ereport(ERROR,
111+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
112+
errmsg("money out of range")));
113+
114+
return res;
115+
}
116+
117+
static inline Cash
118+
cash_mul_float8(Cash c, float8 f)
119+
{
120+
float8 res = rint(float8_mul((float8) c, f));
121+
122+
if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
123+
ereport(ERROR,
124+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
125+
errmsg("money out of range")));
126+
127+
return (Cash) res;
128+
}
129+
130+
static inline Cash
131+
cash_div_float8(Cash c, float8 f)
132+
{
133+
float8 res = rint(float8_div((float8) c, f));
134+
135+
if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
136+
ereport(ERROR,
137+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
138+
errmsg("money out of range")));
139+
140+
return (Cash) res;
141+
}
142+
143+
static inline Cash
144+
cash_mul_int64(Cash c, int64 i)
145+
{
146+
Cash res;
147+
148+
if (unlikely(pg_mul_s64_overflow(c, i, &res)))
149+
ereport(ERROR,
150+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
151+
errmsg("money out of range")));
152+
153+
return res;
154+
}
155+
156+
static inline Cash
157+
cash_div_int64(Cash c, int64 i)
158+
{
159+
if (unlikely(i == 0))
160+
ereport(ERROR,
161+
(errcode(ERRCODE_DIVISION_BY_ZERO),
162+
errmsg("division by zero")));
163+
164+
return c / i;
165+
}
166+
90167
/* cash_in()
91168
* Convert a string to a cash data type.
92169
* 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
@@ -503,3 +503,22 @@ SELECT '-92233720368547758.08'::money::numeric;
503503
-92233720368547758.08
504504
(1 row)
505505

506+
-- overflow checks
507+
SELECT '92233720368547758.07'::money + '0.01'::money;
508+
ERROR: money out of range
509+
SELECT '-92233720368547758.08'::money - '0.01'::money;
510+
ERROR: money out of range
511+
SELECT '92233720368547758.07'::money * 2::float8;
512+
ERROR: money out of range
513+
SELECT '-1'::money / 1.175494e-38::float4;
514+
ERROR: money out of range
515+
SELECT '92233720368547758.07'::money * 2::int4;
516+
ERROR: money out of range
517+
SELECT '1'::money / 0::int2;
518+
ERROR: division by zero
519+
SELECT '42'::money * 'inf'::float8;
520+
ERROR: money out of range
521+
SELECT '42'::money * '-inf'::float8;
522+
ERROR: money out of range
523+
SELECT '42'::money * 'nan'::float4;
524+
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
@@ -129,3 +129,14 @@ SELECT '12345678901234567'::money::numeric;
129129
SELECT '-12345678901234567'::money::numeric;
130130
SELECT '92233720368547758.07'::money::numeric;
131131
SELECT '-92233720368547758.08'::money::numeric;
132+
133+
-- overflow checks
134+
SELECT '92233720368547758.07'::money + '0.01'::money;
135+
SELECT '-92233720368547758.08'::money - '0.01'::money;
136+
SELECT '92233720368547758.07'::money * 2::float8;
137+
SELECT '-1'::money / 1.175494e-38::float4;
138+
SELECT '92233720368547758.07'::money * 2::int4;
139+
SELECT '1'::money / 0::int2;
140+
SELECT '42'::money * 'inf'::float8;
141+
SELECT '42'::money * '-inf'::float8;
142+
SELECT '42'::money * 'nan'::float4;

0 commit comments

Comments
 (0)