1414 * Copyright (c) 1998-2003, PostgreSQL Global Development Group
1515 *
1616 * IDENTIFICATION
17- * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.73 2004/05/07 00:24:58 tgl Exp $
17+ * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.74 2004/05/14 21:42:28 neilc Exp $
1818 *
1919 *-------------------------------------------------------------------------
2020 */
@@ -252,6 +252,7 @@ static Numeric make_result(NumericVar *var);
252252
253253static void apply_typmod (NumericVar * var ,int32 typmod );
254254
255+ static int32 numericvar_to_int4 (NumericVar * var );
255256static bool numericvar_to_int8 (NumericVar * var ,int64 * result );
256257static void int8_to_numericvar (int64 val ,NumericVar * var );
257258static double numeric_to_double_no_overflow (Numeric num );
@@ -285,6 +286,8 @@ static void sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result);
285286static void round_var (NumericVar * var ,int rscale );
286287static void trunc_var (NumericVar * var ,int rscale );
287288static void strip_var (NumericVar * var );
289+ static void compute_bucket (Numeric operand ,Numeric bound1 ,Numeric bound2 ,
290+ NumericVar * count_var ,NumericVar * result_var );
288291
289292
290293/* ----------------------------------------------------------------------
@@ -803,6 +806,125 @@ numeric_floor(PG_FUNCTION_ARGS)
803806PG_RETURN_NUMERIC (res );
804807}
805808
809+ /*
810+ * width_bucket_numeric() -
811+ *
812+ * 'bound1' and 'bound2' are the lower and upper bounds of the
813+ * histogram's range, respectively. 'count' is the number of buckets
814+ * in the histogram. width_bucket() returns an integer indicating the
815+ * bucket number that 'operand' belongs in for an equiwidth histogram
816+ * with the specified characteristics. An operand smaller than the
817+ * lower bound is assigned to bucket 0. An operand greater than the
818+ * upper bound is assigned to an additional bucket (with number
819+ * count+1).
820+ */
821+ Datum
822+ width_bucket_numeric (PG_FUNCTION_ARGS )
823+ {
824+ Numeric operand = PG_GETARG_NUMERIC (0 );
825+ Numeric bound1 = PG_GETARG_NUMERIC (1 );
826+ Numeric bound2 = PG_GETARG_NUMERIC (2 );
827+ int32 count = PG_GETARG_INT32 (3 );
828+ NumericVar count_var ;
829+ NumericVar result_var ;
830+ int32 result ;
831+
832+ if (count <=0 )
833+ ereport (ERROR ,
834+ (errcode (ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION ),
835+ errmsg ("count must be greater than zero" )));
836+
837+ init_var (& result_var );
838+ init_var (& count_var );
839+
840+ /* Convert 'count' to a numeric, for ease of use later */
841+ int8_to_numericvar ((int64 )count ,& count_var );
842+
843+ switch (cmp_numerics (bound1 ,bound2 ))
844+ {
845+ case 0 :
846+ ereport (ERROR ,
847+ (errcode (ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION ),
848+ errmsg ("lower bound cannot equal upper bound" )));
849+
850+ /* bound1 < bound2 */
851+ case -1 :
852+ if (cmp_numerics (operand ,bound1 )< 0 )
853+ set_var_from_var (& const_zero ,& result_var );
854+ else if (cmp_numerics (operand ,bound2 ) >=0 )
855+ add_var (& count_var ,& const_one ,& result_var );
856+ else
857+ compute_bucket (operand ,bound1 ,bound2 ,
858+ & count_var ,& result_var );
859+ break ;
860+
861+ /* bound1 > bound2 */
862+ case 1 :
863+ if (cmp_numerics (operand ,bound1 )> 0 )
864+ set_var_from_var (& const_zero ,& result_var );
865+ else if (cmp_numerics (operand ,bound2 ) <=0 )
866+ add_var (& count_var ,& const_one ,& result_var );
867+ else
868+ compute_bucket (operand ,bound1 ,bound2 ,
869+ & count_var ,& result_var );
870+ break ;
871+ }
872+
873+ result = numericvar_to_int4 (& result_var );
874+
875+ free_var (& count_var );
876+ free_var (& result_var );
877+
878+ PG_RETURN_INT32 (result );
879+ }
880+
881+ /*
882+ * compute_bucket() -
883+ *
884+ * If 'operand' is not outside the bucket range, determine the correct
885+ * bucket for it to go. The calculations performed by this function
886+ * are derived directly from the SQL2003 spec.
887+ */
888+ static void
889+ compute_bucket (Numeric operand ,Numeric bound1 ,Numeric bound2 ,
890+ NumericVar * count_var ,NumericVar * result_var )
891+ {
892+ NumericVar bound1_var ;
893+ NumericVar bound2_var ;
894+ NumericVar operand_var ;
895+
896+ init_var (& bound1_var );
897+ init_var (& bound2_var );
898+ init_var (& operand_var );
899+
900+ set_var_from_num (bound1 ,& bound1_var );
901+ set_var_from_num (bound2 ,& bound2_var );
902+ set_var_from_num (operand ,& operand_var );
903+
904+ if (cmp_var (& bound1_var ,& bound2_var )< 0 )
905+ {
906+ sub_var (& operand_var ,& bound1_var ,& operand_var );
907+ sub_var (& bound2_var ,& bound1_var ,& bound2_var );
908+ div_var (& operand_var ,& bound2_var ,result_var ,
909+ select_div_scale (& operand_var ,& bound2_var ));
910+ }
911+ else
912+ {
913+ sub_var (& bound1_var ,& operand_var ,& operand_var );
914+ sub_var (& bound1_var ,& bound2_var ,& bound1_var );
915+ div_var (& operand_var ,& bound1_var ,result_var ,
916+ select_div_scale (& operand_var ,& bound1_var ));
917+ }
918+
919+ mul_var (result_var ,count_var ,result_var ,
920+ result_var -> dscale + count_var -> dscale );
921+ add_var (result_var ,& const_one ,result_var );
922+ floor_var (result_var ,result_var );
923+
924+ free_var (& bound1_var );
925+ free_var (& bound2_var );
926+ free_var (& operand_var );
927+ }
806928
807929/* ----------------------------------------------------------------------
808930 *
@@ -1612,7 +1734,6 @@ numeric_int4(PG_FUNCTION_ARGS)
16121734{
16131735Numeric num = PG_GETARG_NUMERIC (0 );
16141736NumericVar x ;
1615- int64 val ;
16161737int32 result ;
16171738
16181739/* XXX would it be better to return NULL? */
@@ -1621,17 +1742,30 @@ numeric_int4(PG_FUNCTION_ARGS)
16211742(errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
16221743errmsg ("cannot convert NaN to integer" )));
16231744
1624- /* Convert to variable format and thence toint8 */
1745+ /* Convert to variable format, then convert toint4 */
16251746init_var (& x );
16261747set_var_from_num (num ,& x );
1748+ result = numericvar_to_int4 (& x );
1749+ free_var (& x );
1750+ PG_RETURN_INT32 (result );
1751+ }
16271752
1628- if (!numericvar_to_int8 (& x ,& val ))
1753+ /*
1754+ * Given a NumericVar, convert it to an int32. If the NumericVar
1755+ * exceeds the range of an int32, raise the appropriate error via
1756+ * ereport(). The input NumericVar is *not* free'd.
1757+ */
1758+ static int32
1759+ numericvar_to_int4 (NumericVar * var )
1760+ {
1761+ int32 result ;
1762+ int64 val ;
1763+
1764+ if (!numericvar_to_int8 (var ,& val ))
16291765ereport (ERROR ,
16301766(errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
16311767errmsg ("integer out of range" )));
16321768
1633- free_var (& x );
1634-
16351769/* Down-convert to int4 */
16361770result = (int32 )val ;
16371771
@@ -1641,10 +1775,9 @@ numeric_int4(PG_FUNCTION_ARGS)
16411775(errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
16421776errmsg ("integer out of range" )));
16431777
1644- PG_RETURN_INT32 ( result ) ;
1778+ return result ;
16451779}
16461780
1647-
16481781Datum
16491782int8_numeric (PG_FUNCTION_ARGS )
16501783{