Index: doc/src/sgml/errcodes.sgml =================================================================== RCS file: /Users/neilc/local/cvs/pgsql-server/doc/src/sgml/errcodes.sgml,v retrieving revision 1.3 diff -c -r1.3 errcodes.sgml *** doc/src/sgml/errcodes.sgml 4 Mar 2004 21:47:18 -0000 1.3 --- doc/src/sgml/errcodes.sgml 25 Apr 2004 02:31:27 -0000 *************** *** 306,311 **** --- 306,316 ---- + 2201G + INVALID ARGUMENT FOR WIDTH BUCKET FUNCTION + + + 22018 INVALID CHARACTER VALUE FOR CAST Index: src/backend/utils/adt/float.c =================================================================== RCS file: /Users/neilc/local/cvs/pgsql-server/src/backend/utils/adt/float.c,v retrieving revision 1.103 diff -c -r1.103 float.c *** src/backend/utils/adt/float.c 1 Apr 2004 23:52:18 -0000 1.103 --- src/backend/utils/adt/float.c 24 Apr 2004 23:51:19 -0000 *************** *** 1825,1830 **** --- 1825,1835 ---- PG_RETURN_INT32(iseed); } + Datum + width_bucket_float8(PG_FUNCTION_ARGS) + { + ; + } /* Index: src/backend/utils/adt/numeric.c =================================================================== RCS file: /Users/neilc/local/cvs/pgsql-server/src/backend/utils/adt/numeric.c,v retrieving revision 1.72 diff -c -r1.72 numeric.c *** src/backend/utils/adt/numeric.c 15 Mar 2004 03:29:22 -0000 1.72 --- src/backend/utils/adt/numeric.c 25 Apr 2004 03:49:04 -0000 *************** *** 252,257 **** --- 252,258 ---- static void apply_typmod(NumericVar *var, int32 typmod); + static int32 numericvar_to_int4(NumericVar *var); static bool numericvar_to_int8(NumericVar *var, int64 *result); static void int8_to_numericvar(int64 val, NumericVar *var); static double numeric_to_double_no_overflow(Numeric num); *************** *** 285,290 **** --- 286,293 ---- static void round_var(NumericVar *var, int rscale); static void trunc_var(NumericVar *var, int rscale); static void strip_var(NumericVar *var); + static void compute_bucket(Numeric operand, Numeric bound1, Numeric bound2, + NumericVar *count_var, NumericVar *result_var); /* ---------------------------------------------------------------------- *************** *** 803,808 **** --- 806,930 ---- PG_RETURN_NUMERIC(res); } + /* + * width_bucket_numeric() - + * + * 'bound1' and 'bound2' are the lower and upper bounds of the + * histogram's range, respectively. 'count' is the number of buckets + * in the histogram. width_bucket() returns an integer indicating the + * bucket number that 'operand' belongs in for an equiwidth histogram + * with the specified characteristics. An operand smaller than the + * lower bound is assigned to bucket 0. An operand greater than the + * upper bound is assigned to an additional bucket (with number + * count+1). + */ + Datum + width_bucket_numeric(PG_FUNCTION_ARGS) + { + Numeric operand = PG_GETARG_NUMERIC(0); + Numeric bound1 = PG_GETARG_NUMERIC(1); + Numeric bound2 = PG_GETARG_NUMERIC(2); + int32 count = PG_GETARG_INT32(3); + NumericVar count_var; + NumericVar result_var; + int32 result; + + if (count <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), + errmsg("count must be greater than zero"))); + + init_var(&result_var); + init_var(&count_var); + + /* Convert 'count' to a numeric, for ease of use later */ + int8_to_numericvar((int64) count, &count_var); + + switch (cmp_numerics(bound1, bound2)) + { + case 0: + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), + errmsg("lower bound cannot equal upper bound"))); + + /* bound1 < bound2 */ + case -1: + if (cmp_numerics(operand, bound1) < 0) + set_var_from_var(&const_zero, &result_var); + else if (cmp_numerics(operand, bound2) >= 0) + add_var(&count_var, &const_one, &result_var); + else + compute_bucket(operand, bound1, bound2, + &count_var, &result_var); + break; + + /* bound1 > bound2 */ + case 1: + if (cmp_numerics(operand, bound1) > 0) + set_var_from_var(&const_zero, &result_var); + else if (cmp_numerics(operand, bound2) <= 0) + add_var(&count_var, &const_one, &result_var); + else + compute_bucket(operand, bound1, bound2, + &count_var, &result_var); + break; + } + + result = numericvar_to_int4(&result_var); + + free_var(&count_var); + free_var(&result_var); + + PG_RETURN_INT32(result); + } + + /* + * compute_bucket() - + * + * If 'operand' is not outside the bucket range, determine the correct + * bucket for it to go. The calculations performed by this function + * are derived directly from the SQL2003 spec. + */ + static void + compute_bucket(Numeric operand, Numeric bound1, Numeric bound2, + NumericVar *count_var, NumericVar *result_var) + { + NumericVar bound1_var; + NumericVar bound2_var; + NumericVar operand_var; + + init_var(&bound1_var); + init_var(&bound2_var); + init_var(&operand_var); + + set_var_from_num(bound1, &bound1_var); + set_var_from_num(bound2, &bound2_var); + set_var_from_num(operand, &operand_var); + + if (cmp_var(&bound1_var, &bound2_var) < 0) + { + sub_var(&operand_var, &bound1_var, &operand_var); + sub_var(&bound2_var, &bound1_var, &bound2_var); + div_var(&operand_var, &bound2_var, result_var, + select_div_scale(&operand_var, &bound2_var)); + } + else + { + sub_var(&bound1_var, &operand_var, &operand_var); + sub_var(&bound1_var, &bound2_var, &bound1_var); + div_var(&operand_var, &bound1_var, result_var, + select_div_scale(&operand_var, &bound1_var)); + } + + mul_var(result_var, count_var, result_var, + result_var->dscale + count_var->dscale); + add_var(result_var, &const_one, result_var); + floor_var(result_var, result_var); + + free_var(&bound1_var); + free_var(&bound2_var); + free_var(&operand_var); + } /* ---------------------------------------------------------------------- * *************** *** 1612,1618 **** { Numeric num = PG_GETARG_NUMERIC(0); NumericVar x; - int64 val; int32 result; /* XXX would it be better to return NULL? */ --- 1734,1739 ---- *************** *** 1621,1637 **** (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert NaN to integer"))); ! /* Convert to variable format and thence to int8 */ init_var(&x); set_var_from_num(num, &x); ! if (!numericvar_to_int8(&x, &val)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); - free_var(&x); - /* Down-convert to int4 */ result = (int32) val; --- 1742,1771 ---- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert NaN to integer"))); ! /* Convert to variable format, then convert to int4 */ init_var(&x); set_var_from_num(num, &x); + result = numericvar_to_int4(&x); + free_var(&x); + PG_RETURN_INT32(result); + } + + /* + * Given a NumericVar, convert it to an int32. If the NumericVar + * exceeds the range of an int32, raise the appropriate error via + * ereport(). The input NumericVar is *not* free'd. + */ + static int32 + numericvar_to_int4(NumericVar *var) + { + int32 result; + int64 val; ! if (!numericvar_to_int8(var, &val)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); /* Down-convert to int4 */ result = (int32) val; *************** *** 1641,1649 **** (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); ! PG_RETURN_INT32(result); } - Datum int8_numeric(PG_FUNCTION_ARGS) --- 1775,1782 ---- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); ! return result; } Datum int8_numeric(PG_FUNCTION_ARGS) Index: src/include/catalog/catversion.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql-server/src/include/catalog/catversion.h,v retrieving revision 1.224 diff -c -r1.224 catversion.h *** src/include/catalog/catversion.h 23 Apr 2004 20:32:19 -0000 1.224 --- src/include/catalog/catversion.h 24 Apr 2004 23:51:19 -0000 *************** *** 53,58 **** */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200404220 #endif --- 53,58 ---- */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200404230 #endif Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql-server/src/include/catalog/pg_proc.h,v retrieving revision 1.325 diff -c -r1.325 pg_proc.h *** src/include/catalog/pg_proc.h 23 Apr 2004 20:32:19 -0000 1.325 --- src/include/catalog/pg_proc.h 25 Apr 2004 03:30:52 -0000 *************** *** 2508,2513 **** --- 2508,2517 ---- DESCR("(internal)"); DATA(insert OID = 1746 ( float8 PGNSP PGUID 12 f f t f i 1 701 "1700" _null_ numeric_float8 - _null_ )); DESCR("(internal)"); + DATA(insert OID = 2170 ( width_bucket PGNSP PGUID 12 f f t f i 4 23 "1700 1700 1700 23" _null_ width_bucket_numeric - _null_ )); + DESCR("XXX: todo"); + DATA(insert OID = 2171 ( width_bucket PGNSP PGUID 12 f f t f i 4 23 "701 701 701 23" _null_ width_bucket_float8 - _null_ )); + DESCR("XXX: todo"); DATA(insert OID = 1747 ( time_pl_interval PGNSP PGUID 12 f f t f i 2 1083 "1083 1186" _null_ time_pl_interval - _null_ )); DESCR("plus"); Index: src/include/utils/builtins.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql-server/src/include/utils/builtins.h,v retrieving revision 1.236 diff -c -r1.236 builtins.h *** src/include/utils/builtins.h 1 Apr 2004 21:28:46 -0000 1.236 --- src/include/utils/builtins.h 24 Apr 2004 23:51:19 -0000 *************** *** 347,352 **** --- 347,353 ---- extern Datum float84le(PG_FUNCTION_ARGS); extern Datum float84gt(PG_FUNCTION_ARGS); extern Datum float84ge(PG_FUNCTION_ARGS); + extern Datum width_bucket_float8(PG_FUNCTION_ARGS); /* misc.c */ extern Datum nullvalue(PG_FUNCTION_ARGS); *************** *** 756,761 **** --- 757,763 ---- extern Datum int2_avg_accum(PG_FUNCTION_ARGS); extern Datum int4_avg_accum(PG_FUNCTION_ARGS); extern Datum int8_avg(PG_FUNCTION_ARGS); + extern Datum width_bucket_numeric(PG_FUNCTION_ARGS); /* ri_triggers.c */ extern Datum RI_FKey_check_ins(PG_FUNCTION_ARGS); Index: src/include/utils/errcodes.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql-server/src/include/utils/errcodes.h,v retrieving revision 1.8 diff -c -r1.8 errcodes.h *** src/include/utils/errcodes.h 4 Mar 2004 21:47:18 -0000 1.8 --- src/include/utils/errcodes.h 25 Apr 2004 02:21:35 -0000 *************** *** 100,105 **** --- 100,106 ---- #define ERRCODE_ESCAPE_CHARACTER_CONFLICT MAKE_SQLSTATE('2','2', '0','0','B') #define ERRCODE_INDICATOR_OVERFLOW MAKE_SQLSTATE('2','2', '0','2','2') #define ERRCODE_INTERVAL_FIELD_OVERFLOW MAKE_SQLSTATE('2','2', '0','1','5') + #define ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION MAKE_SQLSTATE('2','2', '0', '1', 'G') #define ERRCODE_INVALID_CHARACTER_VALUE_FOR_CAST MAKE_SQLSTATE('2','2', '0','1','8') #define ERRCODE_INVALID_DATETIME_FORMAT MAKE_SQLSTATE('2','2', '0','0','7') #define ERRCODE_INVALID_ESCAPE_CHARACTER MAKE_SQLSTATE('2','2', '0','1','9') Index: src/test/regress/expected/numeric.out =================================================================== RCS file: /Users/neilc/local/cvs/pgsql-server/src/test/regress/expected/numeric.out,v retrieving revision 1.14 diff -c -r1.14 numeric.out *** src/test/regress/expected/numeric.out 23 Apr 2004 20:32:20 -0000 1.14 --- src/test/regress/expected/numeric.out 25 Apr 2004 03:58:35 -0000 *************** *** 730,735 **** --- 730,786 ---- (7 rows) DROP TABLE ceil_floor_round; + -- Testing for width_bucket() + -- NULL result + SELECT width_bucket(NULL, NULL, NULL, NULL); + width_bucket + -------------- + + (1 row) + + -- errors + SELECT width_bucket(5.0, 3.0, 4.0, 0); + ERROR: count must be greater than zero + SELECT width_bucket(5.0, 3.0, 4.0, -5); + ERROR: count must be greater than zero + SELECT width_bucket(3.0, 3.0, 3.0, 888); + ERROR: lower bound cannot equal upper bound + -- normal operation + CREATE TABLE width_bucket_test (operand numeric); + COPY width_bucket_test FROM stdin; + SELECT + operand, + width_bucket(operand, 0, 10, 5) AS wb_1, + width_bucket(operand, 10, 0, 5) AS wb_2, + width_bucket(operand, 2, 8, 4) AS wb_3, + width_bucket(operand, 5.0, 5.5, 20) AS wb_4, + width_bucket(operand, -25, 25, 10) AS wb_5 + FROM width_bucket_test; + operand | wb_1 | wb_2 | wb_3 | wb_4 | wb_5 + ------------------+------+------+------+------+------ + -5.2 | 0 | 6 | 0 | 0 | 4 + -0.0000000000001 | 0 | 6 | 0 | 0 | 5 + 0.0000000000001 | 1 | 5 | 0 | 0 | 6 + 1 | 1 | 5 | 0 | 0 | 6 + 1.99999999999999 | 1 | 5 | 0 | 0 | 6 + 2 | 2 | 5 | 1 | 0 | 6 + 2.00000000000001 | 2 | 4 | 1 | 0 | 6 + 3 | 2 | 4 | 1 | 0 | 6 + 4 | 3 | 4 | 2 | 0 | 6 + 4.5 | 3 | 3 | 2 | 0 | 6 + 5 | 3 | 3 | 3 | 1 | 7 + 5.5 | 3 | 3 | 3 | 21 | 7 + 6 | 4 | 3 | 3 | 21 | 7 + 7 | 4 | 2 | 4 | 21 | 7 + 8 | 5 | 2 | 5 | 21 | 7 + 9 | 5 | 1 | 5 | 21 | 7 + 9.99999999999999 | 5 | 1 | 5 | 21 | 7 + 10 | 6 | 1 | 5 | 21 | 8 + 10.0000000000001 | 6 | 0 | 5 | 21 | 8 + NaN | 6 | 0 | 5 | 21 | 11 + (20 rows) + + DROP TABLE width_bucket_test; -- TO_CHAR() -- SELECT '' AS to_char_1, to_char(val, '9G999G999G999G999G999') Index: src/test/regress/sql/numeric.sql =================================================================== RCS file: /Users/neilc/local/cvs/pgsql-server/src/test/regress/sql/numeric.sql,v retrieving revision 1.9 diff -c -r1.9 numeric.sql *** src/test/regress/sql/numeric.sql 23 Apr 2004 20:32:20 -0000 1.9 --- src/test/regress/sql/numeric.sql 25 Apr 2004 03:53:50 -0000 *************** *** 667,672 **** --- 667,718 ---- SELECT a, ceil(a), ceiling(a), floor(a), round(a) FROM ceil_floor_round; DROP TABLE ceil_floor_round; + -- Testing for width_bucket() + -- NULL result + SELECT width_bucket(NULL, NULL, NULL, NULL); + + -- errors + SELECT width_bucket(5.0, 3.0, 4.0, 0); + SELECT width_bucket(5.0, 3.0, 4.0, -5); + SELECT width_bucket(3.0, 3.0, 3.0, 888); + + -- normal operation + CREATE TABLE width_bucket_test (operand numeric); + + COPY width_bucket_test FROM stdin; + -5.2 + -0.0000000000001 + 0.0000000000001 + 1 + 1.99999999999999 + 2 + 2.00000000000001 + 3 + 4 + 4.5 + 5 + 5.5 + 6 + 7 + 8 + 9 + 9.99999999999999 + 10 + 10.0000000000001 + NaN + \. + + SELECT + operand, + width_bucket(operand, 0, 10, 5) AS wb_1, + width_bucket(operand, 10, 0, 5) AS wb_2, + width_bucket(operand, 2, 8, 4) AS wb_3, + width_bucket(operand, 5.0, 5.5, 20) AS wb_4, + width_bucket(operand, -25, 25, 10) AS wb_5 + FROM width_bucket_test; + + DROP TABLE width_bucket_test; + -- TO_CHAR() -- SELECT '' AS to_char_1, to_char(val, '9G999G999G999G999G999')