From 31e69ccb210cf49712f77facbf90661d8bc2eed5 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 11 Mar 2003 21:01:33 +0000 Subject: [PATCH] Add explicit tests for division by zero to all user-accessible integer division and modulo functions, to avoid problems on OS X (which fails to trap 0 divide at all) and Windows (which traps it in some bizarre nonstandard fashion). Standardize on 'division by zero' as the one true spelling of this error message. Add regression tests as suggested by Neil Conway. --- src/backend/utils/adt/cash.c | 10 ++--- src/backend/utils/adt/char.c | 5 ++- src/backend/utils/adt/float.c | 10 ++--- src/backend/utils/adt/geo_ops.c | 4 +- src/backend/utils/adt/int.c | 26 +++++++++++- src/backend/utils/adt/int8.c | 14 ++++++- src/backend/utils/adt/numeric.c | 4 +- src/backend/utils/adt/timestamp.c | 4 +- src/test/regress/expected/errors.out | 41 ++++++++++++++---- .../expected/float4-exp-three-digits.out | 2 +- src/test/regress/expected/float4.out | 2 +- .../expected/float8-exp-three-digits.out | 2 +- .../regress/expected/float8-fp-exception.out | 2 +- .../regress/expected/float8-small-is-zero.out | 2 +- src/test/regress/expected/float8.out | 2 +- src/test/regress/sql/errors.sql | 42 +++++++++++++++---- 16 files changed, 130 insertions(+), 42 deletions(-) diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c index c33bca654e..f210f4258f 100644 --- a/src/backend/utils/adt/cash.c +++ b/src/backend/utils/adt/cash.c @@ -9,7 +9,7 @@ * workings can be found in the book "Software Solutions in C" by * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7. * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.56 2002/09/04 20:31:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.57 2003/03/11 21:01:33 tgl Exp $ */ #include "postgres.h" @@ -442,7 +442,7 @@ cash_div_flt8(PG_FUNCTION_ARGS) Cash result; if (f == 0.0) - elog(ERROR, "cash_div: divide by 0.0 error"); + elog(ERROR, "division by zero"); result = rint(c / f); PG_RETURN_CASH(result); @@ -492,7 +492,7 @@ cash_div_flt4(PG_FUNCTION_ARGS) Cash result; if (f == 0.0) - elog(ERROR, "cash_div: divide by 0.0 error"); + elog(ERROR, "division by zero"); result = rint(c / f); PG_RETURN_CASH(result); @@ -543,7 +543,7 @@ cash_div_int4(PG_FUNCTION_ARGS) Cash result; if (i == 0) - elog(ERROR, "cash_div_int4: divide by 0 error"); + elog(ERROR, "division by zero"); result = rint(c / i); @@ -593,7 +593,7 @@ cash_div_int2(PG_FUNCTION_ARGS) Cash result; if (s == 0) - elog(ERROR, "cash_div: divide by 0 error"); + elog(ERROR, "division by zero"); result = rint(c / s); PG_RETURN_CASH(result); diff --git a/src/backend/utils/adt/char.c b/src/backend/utils/adt/char.c index c31995fdf0..0e2d400aa0 100644 --- a/src/backend/utils/adt/char.c +++ b/src/backend/utils/adt/char.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/char.c,v 1.33 2002/06/20 20:29:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/char.c,v 1.34 2003/03/11 21:01:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -150,6 +150,9 @@ chardiv(PG_FUNCTION_ARGS) char arg1 = PG_GETARG_CHAR(0); char arg2 = PG_GETARG_CHAR(1); + if (arg2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_CHAR((int8) arg1 / (int8) arg2); } diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index c0acc554b8..4220b4775a 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.83 2002/11/08 17:37:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.84 2003/03/11 21:01:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -505,7 +505,7 @@ float4div(PG_FUNCTION_ARGS) double result; if (arg2 == 0.0) - elog(ERROR, "float4div: divide by zero error"); + elog(ERROR, "division by zero"); /* Do division in float8, then check for overflow */ result = (float8) arg1 / (float8) arg2; @@ -567,7 +567,7 @@ float8div(PG_FUNCTION_ARGS) float8 result; if (arg2 == 0.0) - elog(ERROR, "float8div: divide by zero error"); + elog(ERROR, "division by zero"); result = arg1 / arg2; @@ -1753,7 +1753,7 @@ float48div(PG_FUNCTION_ARGS) float8 result; if (arg2 == 0.0) - elog(ERROR, "float48div: divide by zero"); + elog(ERROR, "division by zero"); result = arg1 / arg2; CheckFloat8Val(result); @@ -1813,7 +1813,7 @@ float84div(PG_FUNCTION_ARGS) float8 result; if (arg2 == 0.0) - elog(ERROR, "float84div: divide by zero"); + elog(ERROR, "division by zero"); result = arg1 / arg2; diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index 9fe40d2ccb..d8d1f7c3af 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/geo_ops.c,v 1.74 2003/01/21 19:44:26 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/geo_ops.c,v 1.75 2003/03/11 21:01:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3504,7 +3504,7 @@ point_div(PG_FUNCTION_ARGS) div = (p2->x * p2->x) + (p2->y * p2->y); if (div == 0.0) - elog(ERROR, "point_div: divide by 0.0 error"); + elog(ERROR, "division by zero"); result->x = ((p1->x * p2->x) + (p1->y * p2->y)) / div; result->y = ((p2->x * p1->y) - (p2->y * p1->x)) / div; diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 859e78b58c..73687d55ec 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.52 2002/08/22 00:01:43 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.53 2003/03/11 21:01:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -551,6 +551,9 @@ int4div(PG_FUNCTION_ARGS) int32 arg1 = PG_GETARG_INT32(0); int32 arg2 = PG_GETARG_INT32(1); + if (arg2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT32(arg1 / arg2); } @@ -611,6 +614,9 @@ int2div(PG_FUNCTION_ARGS) int16 arg1 = PG_GETARG_INT16(0); int16 arg2 = PG_GETARG_INT16(1); + if (arg2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT16(arg1 / arg2); } @@ -647,6 +653,9 @@ int24div(PG_FUNCTION_ARGS) int16 arg1 = PG_GETARG_INT16(0); int32 arg2 = PG_GETARG_INT32(1); + if (arg2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT32(arg1 / arg2); } @@ -683,6 +692,9 @@ int42div(PG_FUNCTION_ARGS) int32 arg1 = PG_GETARG_INT32(0); int16 arg2 = PG_GETARG_INT16(1); + if (arg2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT32(arg1 / arg2); } @@ -692,6 +704,9 @@ int4mod(PG_FUNCTION_ARGS) int32 arg1 = PG_GETARG_INT32(0); int32 arg2 = PG_GETARG_INT32(1); + if (arg2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT32(arg1 % arg2); } @@ -701,6 +716,9 @@ int2mod(PG_FUNCTION_ARGS) int16 arg1 = PG_GETARG_INT16(0); int16 arg2 = PG_GETARG_INT16(1); + if (arg2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT16(arg1 % arg2); } @@ -710,6 +728,9 @@ int24mod(PG_FUNCTION_ARGS) int16 arg1 = PG_GETARG_INT16(0); int32 arg2 = PG_GETARG_INT32(1); + if (arg2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT32(arg1 % arg2); } @@ -719,6 +740,9 @@ int42mod(PG_FUNCTION_ARGS) int32 arg1 = PG_GETARG_INT32(0); int16 arg2 = PG_GETARG_INT16(1); + if (arg2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT32(arg1 % arg2); } diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 8a346cd8b8..cf16456105 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/int8.c,v 1.42 2002/09/18 21:35:22 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/int8.c,v 1.43 2003/03/11 21:01:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -504,6 +504,9 @@ int8div(PG_FUNCTION_ARGS) int64 val1 = PG_GETARG_INT64(0); int64 val2 = PG_GETARG_INT64(1); + if (val2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT64(val1 / val2); } @@ -528,6 +531,9 @@ int8mod(PG_FUNCTION_ARGS) int64 val2 = PG_GETARG_INT64(1); int64 result; + if (val2 == 0) + elog(ERROR, "division by zero"); + result = val1 / val2; result *= val2; result = val1 - result; @@ -621,6 +627,9 @@ int84div(PG_FUNCTION_ARGS) int64 val1 = PG_GETARG_INT64(0); int32 val2 = PG_GETARG_INT32(1); + if (val2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT64(val1 / val2); } @@ -657,6 +666,9 @@ int48div(PG_FUNCTION_ARGS) int32 val1 = PG_GETARG_INT32(0); int64 val2 = PG_GETARG_INT64(1); + if (val2 == 0) + elog(ERROR, "division by zero"); + PG_RETURN_INT64(val1 / val2); } diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index f1967019d5..1a21500a8b 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -5,7 +5,7 @@ * * 1998 Jan Wieck * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.56 2002/10/19 02:08:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.57 2003/03/11 21:01:33 tgl Exp $ * * ---------- */ @@ -3266,7 +3266,7 @@ div_var(NumericVar *var1, NumericVar *var2, NumericVar *result) */ ndigits_tmp = var2->ndigits + 1; if (ndigits_tmp == 1) - elog(ERROR, "division by zero on numeric"); + elog(ERROR, "division by zero"); /* * Determine the result sign, weight and number of digits to calculate diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index d3390fd327..2764fee906 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.79 2003/02/27 21:36:58 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.80 2003/03/11 21:01:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1869,7 +1869,7 @@ interval_div(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); if (factor == 0.0) - elog(ERROR, "interval_div: divide by 0.0 error"); + elog(ERROR, "division by zero"); #ifdef HAVE_INT64_TIMESTAMP result->month = (span->month / factor); diff --git a/src/test/regress/expected/errors.out b/src/test/regress/expected/errors.out index 698ccc8565..6c38629217 100644 --- a/src/test/regress/expected/errors.out +++ b/src/test/regress/expected/errors.out @@ -18,7 +18,7 @@ select 1; -- notify pg_class -- -- --- RETRIEVE +-- SELECT -- missing relation name select; @@ -54,7 +54,7 @@ ERROR: parser: parse error at or near ";" at character 12 delete from nonesuch; ERROR: Relation "nonesuch" does not exist -- --- DESTROY +-- DROP -- missing relation name (this had better not wildcard!) drop table; @@ -63,7 +63,7 @@ ERROR: parser: parse error at or near ";" at character 11 drop table nonesuch; ERROR: table "nonesuch" does not exist -- --- RENAME +-- ALTER TABLE -- relation renaming -- missing relation name @@ -104,7 +104,7 @@ WARNING: ROLLBACK: no transaction in progress end; WARNING: COMMIT: no transaction in progress -- --- DEFINE AGGREGATE +-- CREATE AGGREGATE -- sfunc/finalfunc type disagreement create aggregate newavg2 (sfunc = int4pl, basetype = int4, @@ -118,7 +118,7 @@ create aggregate newcnt1 (sfunc = int4inc, initcond = '0'); ERROR: Define: "basetype" unspecified -- --- REMOVE INDEX +-- DROP INDEX -- missing index name drop index; @@ -130,7 +130,7 @@ ERROR: parser: parse error at or near "314159" at character 12 drop index nonesuch; ERROR: index "nonesuch" does not exist -- --- REMOVE AGGREGATE +-- DROP AGGREGATE -- missing aggregate name drop aggregate; @@ -151,7 +151,7 @@ ERROR: RemoveAggregate: aggregate nonesuch(integer) does not exist drop aggregate newcnt (float4); ERROR: RemoveAggregate: aggregate newcnt(real) does not exist -- --- REMOVE FUNCTION +-- DROP FUNCTION -- missing function name drop function (); @@ -163,7 +163,7 @@ ERROR: parser: parse error at or near "314159" at character 15 drop function nonesuch(); ERROR: RemoveFunction: function nonesuch() does not exist -- --- REMOVE TYPE +-- DROP TYPE -- missing type name drop type; @@ -237,3 +237,28 @@ ERROR: parser: parse error at or near "instance" at character 6 -- no such rule drop rewrite rule nonesuch; ERROR: parser: parse error at or near "rewrite" at character 6 +-- +-- Check that division-by-zero is properly caught. +-- +select 1/0; +ERROR: division by zero +select 1::int8/0; +ERROR: division by zero +select 1/0::int8; +ERROR: division by zero +select 1::int2/0; +ERROR: division by zero +select 1/0::int2; +ERROR: division by zero +select 1::numeric/0; +ERROR: division by zero +select 1/0::numeric; +ERROR: division by zero +select 1::float8/0; +ERROR: division by zero +select 1/0::float8; +ERROR: division by zero +select 1::float4/0; +ERROR: division by zero +select 1/0::float4; +ERROR: division by zero diff --git a/src/test/regress/expected/float4-exp-three-digits.out b/src/test/regress/expected/float4-exp-three-digits.out index 8efd434a85..72751ae29e 100644 --- a/src/test/regress/expected/float4-exp-three-digits.out +++ b/src/test/regress/expected/float4-exp-three-digits.out @@ -113,7 +113,7 @@ SELECT '' AS three, f.f1, f.f1 - '-10' AS x FROM FLOAT4_TBL f -- test divide by zero SELECT '' AS bad, f.f1 / '0.0' from FLOAT4_TBL f; -ERROR: float4div: divide by zero error +ERROR: division by zero SELECT '' AS five, FLOAT4_TBL.*; five | f1 ------+-------------- diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out index e03db639c5..7ec463b7f3 100644 --- a/src/test/regress/expected/float4.out +++ b/src/test/regress/expected/float4.out @@ -113,7 +113,7 @@ SELECT '' AS three, f.f1, f.f1 - '-10' AS x FROM FLOAT4_TBL f -- test divide by zero SELECT '' AS bad, f.f1 / '0.0' from FLOAT4_TBL f; -ERROR: float4div: divide by zero error +ERROR: division by zero SELECT '' AS five, FLOAT4_TBL.*; five | f1 ------+------------- diff --git a/src/test/regress/expected/float8-exp-three-digits.out b/src/test/regress/expected/float8-exp-three-digits.out index b346ed5c38..9d38aee196 100644 --- a/src/test/regress/expected/float8-exp-three-digits.out +++ b/src/test/regress/expected/float8-exp-three-digits.out @@ -257,7 +257,7 @@ ERROR: can't take log of a negative number SELECT '' AS bad, exp(f.f1) from FLOAT8_TBL f; ERROR: exp() result is out of range SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f; -ERROR: float8div: divide by zero error +ERROR: division by zero SELECT '' AS five, FLOAT8_TBL.*; five | f1 ------+----------------------- diff --git a/src/test/regress/expected/float8-fp-exception.out b/src/test/regress/expected/float8-fp-exception.out index 15d96ff365..5c2df1e7c0 100644 --- a/src/test/regress/expected/float8-fp-exception.out +++ b/src/test/regress/expected/float8-fp-exception.out @@ -257,7 +257,7 @@ ERROR: can't take log of a negative number SELECT '' AS bad, exp(f.f1) from FLOAT8_TBL f; ERROR: exp() result is out of range SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f; -ERROR: float8div: divide by zero error +ERROR: division by zero SELECT '' AS five, FLOAT8_TBL.*; five | f1 ------+----------------------- diff --git a/src/test/regress/expected/float8-small-is-zero.out b/src/test/regress/expected/float8-small-is-zero.out index 119051f399..448bfc40bc 100644 --- a/src/test/regress/expected/float8-small-is-zero.out +++ b/src/test/regress/expected/float8-small-is-zero.out @@ -257,7 +257,7 @@ ERROR: can't take log of a negative number SELECT '' AS bad, exp(f.f1) from FLOAT8_TBL f; ERROR: exp() result is out of range SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f; -ERROR: float8div: divide by zero error +ERROR: division by zero SELECT '' AS five, FLOAT8_TBL.*; five | f1 ------+----------------------- diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out index d91427c6ff..576768785f 100644 --- a/src/test/regress/expected/float8.out +++ b/src/test/regress/expected/float8.out @@ -257,7 +257,7 @@ ERROR: can't take log of a negative number SELECT '' AS bad, exp(f.f1) from FLOAT8_TBL f; ERROR: exp() result is out of range SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f; -ERROR: float8div: divide by zero error +ERROR: division by zero SELECT '' AS five, FLOAT8_TBL.*; five | f1 ------+----------------------- diff --git a/src/test/regress/sql/errors.sql b/src/test/regress/sql/errors.sql index dee19bc136..5876244ee3 100644 --- a/src/test/regress/sql/errors.sql +++ b/src/test/regress/sql/errors.sql @@ -17,7 +17,7 @@ select 1; -- -- --- RETRIEVE +-- SELECT -- missing relation name select; @@ -55,7 +55,7 @@ delete from nonesuch; -- --- DESTROY +-- DROP -- missing relation name (this had better not wildcard!) drop table; @@ -65,9 +65,8 @@ drop table nonesuch; -- --- RENAME +-- ALTER TABLE - -- relation renaming -- missing relation name @@ -112,7 +111,7 @@ end; -- --- DEFINE AGGREGATE +-- CREATE AGGREGATE -- sfunc/finalfunc type disagreement create aggregate newavg2 (sfunc = int4pl, @@ -128,7 +127,7 @@ create aggregate newcnt1 (sfunc = int4inc, -- --- REMOVE INDEX +-- DROP INDEX -- missing index name drop index; @@ -141,7 +140,7 @@ drop index nonesuch; -- --- REMOVE AGGREGATE +-- DROP AGGREGATE -- missing aggregate name drop aggregate; @@ -163,7 +162,7 @@ drop aggregate newcnt (float4); -- --- REMOVE FUNCTION +-- DROP FUNCTION -- missing function name drop function (); @@ -176,7 +175,7 @@ drop function nonesuch(); -- --- REMOVE TYPE +-- DROP TYPE -- missing type name drop type; @@ -252,3 +251,28 @@ drop instance rule nonesuch on noplace; -- no such rule drop rewrite rule nonesuch; +-- +-- Check that division-by-zero is properly caught. +-- + +select 1/0; + +select 1::int8/0; + +select 1/0::int8; + +select 1::int2/0; + +select 1/0::int2; + +select 1::numeric/0; + +select 1/0::numeric; + +select 1::float8/0; + +select 1/0::float8; + +select 1::float4/0; + +select 1/0::float4;