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)
803806 PG_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{
16131735 Numeric num = PG_GETARG_NUMERIC (0 );
16141736 NumericVar x ;
1615- int64 val ;
16161737 int32 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 ),
16221743 errmsg ("cannot convert NaN to integer" )));
16231744
1624- /* Convert to variable format and thence to int8 */
1745+ /* Convert to variable format, then convert to int4 */
16251746 init_var (& x );
16261747 set_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 ))
16291765 ereport (ERROR ,
16301766 (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
16311767 errmsg ("integer out of range" )));
16321768
1633- free_var (& x );
1634-
16351769 /* Down-convert to int4 */
16361770 result = (int32 ) val ;
16371771
@@ -1641,10 +1775,9 @@ numeric_int4(PG_FUNCTION_ARGS)
16411775 (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
16421776 errmsg ("integer out of range" )));
16431777
1644- PG_RETURN_INT32 ( result ) ;
1778+ return result ;
16451779}
16461780
1647-
16481781Datum
16491782int8_numeric (PG_FUNCTION_ARGS )
16501783{
0 commit comments